Quickstart

pypi version travis build status

About

Tropostack is a CLI/workflow library that simplifies the creation and management of CloudFormation stacks, based on the excellent Troposphere Project.

Tropostack features:

  • Single stack template = single executable Python file = single CLI
  • Support for different configuration and CLI plugins
  • A collection of generic commands available to each stack (e.g. create)
  • Support for user-defined CLI commands (e.g. upscale)
  • Helper routines (e.g. locate the newest matching AMI)

Installation

$ pip install tropostack

Or, you can use setup.py to install from a cloned repository:

$ python setup.py install

First stack

You use tropostack as a library to:
  • Consisteny define CloudFormation templates in Python code
  • Have a CLI around each stack definition, enabling it to live as a standalone executable

Here is a minimalistic example of a stack that creates an S3 bucket, and exports the ARN as an Output:

#!/usr/bin/env python3

from troposphere import s3
from troposphere import Output, Export, Sub, GetAtt

from tropostack.base import InlineConfStack
from tropostack.cli import InlineConfCLI


class MyS3BucketStack(InlineConfStack):
    # Name of the stack
    BASE_NAME = 'my-s3-bucket-stack'

    # Define configuration values for the stack
    CONF = {
        # Region is always explicitly required
        'region': 'eu-west-1',
        # Prefix the bucket name with the account ID
        'bucket_name': Sub('${AWS::AccountId}-my-first-tropostack-bucket')
    }

    # Stack Resources are defined as class properties prefixed with 'r_'
    @property
    def r_bucket(self):
        return s3.Bucket(
            'MyBucketResource',
            BucketName=self.conf['bucket_name']
        )

    # Stack Outputs are defined as class properties prefixed with 'o_'
    @property
    def o_bucket_arn(self):
        _id = 'BucketArn'
        return Output(
            _id,
            Description='The ARN of the S3 bucket',
            Value=GetAtt(self.r_bucket, 'Arn'),
            # We're exporting the output as <StackName>-<OutputId>
            # Other stacks can read the output relying on the same convention
            Export=Export(Sub("${AWS::StackName}-%s" % _id))
        )


if __name__ == '__main__':
    # Wrap the stack in a CLI and run it
    cli = InlineConfCLI(MyS3BucketStack)
    cli.run()

The above already gives you a usable CLI around your stack definition.

Assuming you put it inside an executable file called s3_minimal.py, you’d be able to call it already:

$ ./s3_minimal.py -h
usage: s3_minimal.py [-h]
                     {apply,create,delete,outputs,print,update,validate}

positional arguments:
  {apply,create,delete,outputs,print,update,validate}

optional arguments:
  -h, --help            show this help message and exit

You can now inspect the “raw” CloudFormation code generated by the stack:

$ ./s3_minimal.py print
Outputs:
  BucketArn:
    Description: The ARN of the S3 bucket
    Export:
      Name: !Sub '${AWS::StackName}-BucketArn'
    Value: !GetAtt 'MyBucketResource.Arn'
Resources:
  MyBucketResource:
    Properties:
      BucketName: !Sub '${AWS::AccountId}-my-first-tropostack-bucket'
    Type: AWS::S3::Bucket

Assuming AWS credentials are present in the environment, we can now fire up stack that would create our S3 bucket:

$ ./s3_minimal.py create
Stack creation initiated for: arn:aws:cloudformation:eu-west-1:472799024263:stack/my-s3-bucket-stack/dd5e93c0-225c-11ea-93d8-0641c159a77a
TIMESTAMP (UTC)          RESOURCE TYPE                              RESOURCE ID                  STATUS                                   REASON
2019-12-19 12:41:23      AWS::CloudFormation::Stack                 my-s3-bucket-stack           CREATE_IN_PROGRESS                       User Initiated
2019-12-19 12:41:26      AWS::S3::Bucket                            MyBucketResource             CREATE_IN_PROGRESS
2019-12-19 12:41:27      AWS::S3::Bucket                            MyBucketResource             CREATE_IN_PROGRESS                       Resource creation Initiated
2019-12-19 12:41:48      AWS::S3::Bucket                            MyBucketResource             CREATE_COMPLETE
2019-12-19 12:41:50      AWS::CloudFormation::Stack                 my-s3-bucket-stack           CREATE_COMPLETE

We can also inspect the stack Outputs - in this case, the ARN of the bucket:

$ ./s3_minimal.py outputs
Stack is in status: CREATE_COMPLETE
OutputKey    OutputValue                                           Description               ExportName
-----------  ----------------------------------------------------  ------------------------  ----------------------------
BucketArn    arn:aws:s3:::472799024263-my-first-tropostack-bucket  The ARN of the S3 bucket  my-s3-bucket-stack-BucketArn

Finally, we can clean up and have our stack deleted:

$ ./s3_minimal.py delete
Destroy initiated for stack: my-s3-bucket-stack
TIMESTAMP (UTC)          RESOURCE TYPE                              RESOURCE ID                  STATUS                                   REASON
2019-12-19 12:44:59      AWS::CloudFormation::Stack                 my-s3-bucket-stack           DELETE_IN_PROGRESS                       User Initiated
Stack is gone: my-s3-bucket-stack (An error occurred (ValidationError) when calling the DescribeStackEvents operation: Stack [my-s3-bucket-stack] does not exist)

Stock commands

While the CLI can be expanded/customized for each individual tropostack, there are several subcommands that come out of the box:

  • print - prints the resulting CloudFormation YAML to the screen
  • validate - Sends the CloudFormation template to the AWS API for validation, and reports back result
  • create - Initiates the stack creation (should only be used if the stack does not exist yet)
  • update - Updates an existing stack (should only be used if the stack exists)
  • apply - Idempotently updates or creates a stack, based on whether it exists or not
  • outputs - Shows the outputs of an existing stack
  • delete - Deletes an existing stack