AWS CloudFormation Support
==========================
When you run ``chalice deploy``, chalice will deploy your application using the
`AWS SDK for Python `__). Chalice also
provides functionality that allows you to manage deployments yourself using
cloudformation. This is provided via the ``chalice package`` command.
When you run this command, chalice will generate the AWS Lambda deployment
package that contains your application as well as a `Serverless Application
Model (SAM) `__
template. You can then use a tool like the AWS CLI, or any cloudformation
deployment tools you use, to deploy your chalice application.
Considerations
--------------
Using the ``chalice package`` command is useful when you don't want to
use ``chalice deploy`` to manage your deployments. There's several reasons
why you might want to do this:
* You have pre-existing infrastructure and tooling set up to manage
cloudformation stacks.
* You want to integrate with other cloudformation stacks to manage
all your AWS resources, including resources outside of your chalice
app.
* You'd like to integrate with `AWS CodePipeline
`__ to automatically deploy
changes when you push to a git repo.
Keep in mind that you can't switch between ``chalice deploy`` and
``chalice package`` + CloudFormation for deploying your app.
If you choose to use ``chalice package`` and CloudFormation to deploy
your app, you won't be able to switch back to ``chalice deploy``.
Running ``chalice deploy`` would create an entirely new set of AWS
resources (API Gateway Rest API, AWS Lambda function, etc).
Template Merge
--------------
It's a common use case to need to modify a Chalice generated template
before deployment. Often to inject extra resources, values, or
configurations that are not supported directly by Chalice. It will
always be the case that something on AWS is not supported by Chalice
directly that a consumer may want to interact with.
The package command can now be invoked with the ``--merge-template`` argument::
$ chalice package --merge-template extras.json out
This extras.json file should be a JSON formatted file which will be
deep-merged on top of the sam.json that is generated by Chalice.
For a simple example lets assume that we have the default new Chalice
project and that extras.json has the following content::
{
"Resources" : {
"MusicTable" : {
"Type" : "AWS::DynamoDB::Table",
"Properties" : {
"TableName" : "MusicData",
"AttributeDefinitions" : [
{
"AttributeName" : "Album",
"AttributeType" : "S"
},
{
"AttributeName" : "Artist",
"AttributeType" : "S"
}
],
"KeySchema" : [
{
"AttributeName" : "Album",
"KeyType" : "HASH"
},
{
"AttributeName" : "Artist",
"KeyType" : "RANGE"
}
],
"ProvisionedThroughput" : {
"ReadCapacityUnits" : "5",
"WriteCapacityUnits" : "5"
}
}
},
"APIHandler": {
"Properties": {
"Environment": {
"Variables": {
"MUSIC_TABLE": {"Ref": "MusicTable"}
}
}
}
}
}
}
The generated template located at out/sam.json will have the DynamoDB table
injected into the resource section, as well as the ``MUSIC_TABLE`` environment
variable added to the ``APIHandler`` Lambda function::
...
"APIHandler": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Runtime": "python3.6",
"Handler": "app.app",
"CodeUri": "./deployment.zip",
"Tags": {
"aws-chalice": "version=1.10-:stage=dev:app=test"
},
"Timeout": 60,
"MemorySize": 128,
"Role": {
"Fn::GetAtt": [
"DefaultRole",
"Arn"
]
},
"Environment": {
"Variables": {
"MUSIC_TABLE": "MusicData"
}
}
}
},
...
This gives us the ability to inject arbitrary resources into our Chalice
applications, and reference them from our Chalice-deployed functions. We can
now rely on Chalice's policy auto-generation to generate a policy that allows
DynamoDB access, inject our own policy modifications through the same
extras.json file, or specify a custom policy using the config file.
Example
-------
In this example, we'll create a chalice app and deploy it using
the AWS CLI.
First install the necessary packages::
$ virtualenv /tmp/venv
$ . /tmp/venv/bin/activate
$ pip install chalice awscli
$ chalice new-project test-cfn-deploy
$ cd test-cfn-deploy
At this point we've installed chalice and the AWS CLI and we have
a basic app created locally. Next we'll run the ``package`` command
and look at its contents::
$ $ chalice package /tmp/packaged-app/
Creating deployment package.
$ ls -la /tmp/packaged-app/
-rw-r--r-- 1 j wheel 3355270 May 25 14:20 deployment.zip
-rw-r--r-- 1 j wheel 3068 May 25 14:20 sam.json
$ unzip -l /tmp/packaged-app/deployment.zip | tail -n 5
17292 05-25-17 14:19 chalice/app.py
283 05-25-17 14:19 chalice/__init__.py
796 05-25-17 14:20 app.py
-------- -------
9826899 723 files
$ head < /tmp/packaged-app/sam.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"RestAPIId": {
"Value": {
"Ref": "RestAPI"
}
},
"APIHandlerName": {
"Value": {
As you can see in the above example, the ``package`` command created a
directory that contained two files, a ``deployment.zip`` file, which is the
Lambda deployment package, and a ``sam.json`` file, which is the SAM template
that can be deployed using CloudFormation. Next we're going to use the AWS CLI
to deploy our app. To this, we'll first run the ``aws cloudformation package``
command, which will take our deployment.zip file and upload to an S3 bucket
we specify::
$ aws cloudformation package \
--template-file /tmp/packaged-app/sam.json \
--s3-bucket myapp-bucket \
--output-template-file /tmp/packaged-app/packaged.yaml
Now we can deploy our app using the ``aws cloudformation deploy`` command::
$ aws cloudformation deploy \
--template-file /tmp/packaged-app/packaged.yaml \
--stack-name test-cfn-stack \
--capabilities CAPABILITY_IAM
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - test-cfn-stack
This will take a few minutes to complete, but once it's done, the endpoint url
will be available as an output::
$ aws cloudformation describe-stacks --stack-name test-cfn-stack \
--query "Stacks[].Outputs[?OutputKey=='EndpointURL'][] | [0].OutputValue"
"https://abc29hkq0i.execute-api.us-west-2.amazonaws.com/api/"
$ http "https://abc29hkq0i.execute-api.us-west-2.amazonaws.com/api/"
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 18
Content-Type: application/json
...
{
"hello": "world"
}