Templates ========= Sceptre uses CloudFormation Templates to launch AWS Stacks. Templates must be stored in a directory named ``templates/``, in the same directory as the ``config/`` directory: .. code-block:: text . ├── config │   └── dev │   ├── config.yaml │   └── vpc.yaml └── templates └── vpc.py CloudFormation -------------- Templates with ``.json`` or ``.yaml`` extensions are treated as CloudFormation Templates. They are read in and used without modification. Jinja ----- Templates with ``.j2`` extensions are treated as Jinja2 Templates. These are rendered and should create a raw JSON or YAML CloudFormation Template. Sceptre User Data is accessible within Templates as ``sceptre_user_data``. For example ``{{ sceptre_user_data.some_variable }}``. ``sceptre_user_data`` accesses the ``sceptre_user_data`` key in the Stack Config file. Example ~~~~~~~ While rendering templates with Jinja2, some characters aren't supported by AWS. A workaround for this is to use Jinja to replace `-`, `.` and `_` with other characters to ensure that we produce a valid template. With this we had some plasticity to the template and it becomes easier to add/remove entries on a particular stack. With this templating some problems will arise, such empty strings as parameters. In the following example you can find a work around for this issues. This represents a stack to deploy route53 records, it's only showing CNAME and ALIAS records to not get too large. Stack: .. code-block:: yaml template: path: templates/dns-extras.j2 type: file dependencies: - prod/route53/domain-zone.yaml parameters: DomainName: "example.com"!stack_output prod/route53/example-com-zone.yaml::FullDomainName Zone: !stack_output prod/route53/example-com-zone.yaml::HostedZoneID sceptre_user_data: CNAMErecords: - record: "example01" address: "example01.otherdomain.com." ttl: 600 - record: "example02" address: "example01.otherdomain.com." ttl: 600 ALIASrecords: - record: "" hostedzoneid: "ZYOURZONEIDDDD" dnsnamealias: "ELB07-Sites-000000000.us-east-1.elb.amazonaws.com" ttl: 600 - record: "www" hostedzoneid: "Z32O12XQLNTSW2" dnsnamealias: "ELB07-Sit es-000000000.us-east-1.elb.amazonaws.com" ttl: 600 Template `dns-extras.j2`: .. code-block:: jinja AWSTemplateFormatVersion: '2010-09-09' Description: 'Add Route53 - CNAME and ALIAS records' Parameters: DomainName: Type: String Default: example.net Zone: Type: String {% if sceptre_user_data.CNAMErecords is defined %}{% for rule in sceptre_user_data.CNAMErecords %} {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}cnamerecord: Type: String Default: "{{rule.record}}"{% endfor %}{% endif %} {% if sceptre_user_data.ALIASrecords is defined %}{% for rule in sceptre_user_data.ALIASrecords %} {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}aliasrecord: Type: String Default: "{{rule.record}}" {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}aliasvalue: Type: String Default: "{{rule.dnsnamealias}}" {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}aliaszoneid: Type: String Default: "{{rule.hostedzoneid}}" {% endfor %}{% endif %} Resources: {% if sceptre_user_data.CNAMErecords is defined %}{% for rule in sceptre_user_data.CNAMErecords %}add{{ rule.record | replace("-","d")|replace("_","s")|replace('.',"p")}}cnamerecord: {% set record = rule.record %} Type: 'AWS::Route53::RecordSet' Properties: Name: !Join - "" - [ !Sub '${ {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}cnamerecord }','.', !Ref DomainName, '.'] HostedZoneId: !Sub '${Zone}' Type: CNAME TTL: {{ rule.ttl }} ResourceRecords: - {{ rule.address }} {% endfor %}{% endif %} {% if sceptre_user_data.ALIASrecords is defined %}{% for rule in sceptre_user_data.ALIASrecords %} {% set entry = rule.record |replace("-","d")|replace("_","s")|replace('.',"p")%}add{{entry}}aliasrecord: Type: AWS::Route53::RecordSet Properties: {% if rule.record == "" %} Name: !Ref DomainName {% else %} Name: !Join - "" - [ !Sub '${ {{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}aliasrecord }','.', !Ref DomainName, '.'] {% endif %} Type: A HostedZoneId: !Ref Zone AliasTarget: DNSName: "{{ rule.dnsnamealias }}" HostedZoneId: "{{ rule.hostedzoneid }}" {% endfor %}{% endif %} Outputs: {% if sceptre_user_data.CNAMErecords is defined %}{% for rule in sceptre_user_data.CNAMErecords %}add{{ rule.record | replace("-","d")|replace("_","s")|replace('.',"p")}}cnamerecord: Value: !Ref 'add{{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}cnamerecord' Description: '{{ rule.address }}' {% endfor %}{% endif %} {% if sceptre_user_data.ALIASrecords is defined %}{% for rule in sceptre_user_data.ALIASrecords %}add{{ rule.record | replace("-","d")|replace("_","s")|replace('.',"p")}}aliasrecord: Value: !Ref 'add{{ rule.record |replace("-","d")|replace("_","s")|replace('.',"p")}}aliasrecord' Description: '{{ rule.dnsnamealias }}' {% endfor %}{% endif %} StackName: Description: 'Stack name.' Value: !Sub '${AWS::StackName}' Export: Name: !Sub '${AWS::StackName}' Python ------ Templates with a ``.py`` extension are treated as Python Templates. They should implement a function named ``sceptre_handler(sceptre_user_data)`` which returns the CloudFormation Template as a ``string``. Sceptre User Data is passed to this function as an argument. If Sceptre User Data is not defined in the Stack Config file, Sceptre passes an empty ``dict``. Example ~~~~~~~ Troposphere ^^^^^^^^^^^ This example is using `troposphere`_ to generate CloudFormation Template as a `json` string. .. code-block:: python from troposphere import Template from troposphere.ec2 import VPC def vpc(sceptre_user_data): """AWS VPC CloudFormationTemplate""" template = Template() template.add_resource(VPC( "VirtualPrivateCloud", CidrBlock=sceptre_user_data["cidr_block"] )) return template.to_yaml() def sceptre_handler(sceptre_user_data): return vpc(sceptre_user_data) .. note:: To generate templates using Troposphere you must install the Troposphere library by running ``pip install sceptre[troposphere]`` .. _troposphere: https://github.com/cloudtools/troposphere/ AWS CDK ^^^^^^^ AWS CDK can be used to programmatically generate templates for your stacks and then Sceptre can deploy those stacks. Taking this approach leverages the best capabilities of CDK (simple template generation with sane defaults) and the best capabilities of Sceptre ("wiring together" stacks and deploying them as consistent environments, leveraging hooks and resolvers to dynamically connect them). In order to use CDK with Sceptre, you need to install the `sceptre_cdk_handler`_ package. You can find documentation and examples on how to use it on the `github repository`_. .. _sceptre_cdk_handler: https://pypi.org/project/sceptre-cdk-handler/ .. _github repository: https://github.com/sceptre/sceptre-cdk-handler AWS SAM ^^^^^^^ There is now a SAM Template Handler that lets you incorporate SAM templates into environments that are managed and deployed using Sceptre. For more information on how to install and use SAM in your Sceptre project, see the `sceptre-sam-handler`_ page on PyPI. .. _sceptre-sam-handler: https://pypi.org/project/sceptre-sam-handler/