{"id":15647621,"url":"https://github.com/cytopia/ansible-role-cloudformation","last_synced_at":"2025-04-30T13:17:58.567Z","repository":{"id":147859116,"uuid":"118235362","full_name":"cytopia/ansible-role-cloudformation","owner":"cytopia","description":"Ansible role to render an arbitrary number of Jinja2 templates into cloudformation files and create any number of stacks.","archived":false,"fork":false,"pushed_at":"2019-06-15T16:57:20.000Z","size":79,"stargazers_count":43,"open_issues_count":1,"forks_count":14,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-01-09T22:08:20.244Z","etag":null,"topics":["ansible","ansible-role","aws","aws-cloudformation","jinja2","jinja2-templates"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cytopia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-20T11:09:59.000Z","updated_at":"2023-09-13T09:43:49.000Z","dependencies_parsed_at":"2023-05-27T17:30:07.836Z","dependency_job_id":null,"html_url":"https://github.com/cytopia/ansible-role-cloudformation","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cytopia%2Fansible-role-cloudformation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cytopia%2Fansible-role-cloudformation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cytopia%2Fansible-role-cloudformation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cytopia%2Fansible-role-cloudformation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cytopia","download_url":"https://codeload.github.com/cytopia/ansible-role-cloudformation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235208996,"owners_count":18953003,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","ansible-role","aws","aws-cloudformation","jinja2","jinja2-templates"],"created_at":"2024-10-03T12:20:20.144Z","updated_at":"2025-01-23T00:53:04.404Z","avatar_url":"https://github.com/cytopia.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ansible Role: Create cloudformation stacks\n\n**[Motivation](#motivation)** |\n**[Installation](#installation)** |\n**[Features](#features)** |\n**[Variables](#variables)** |\n**[Usage](#usage)** |\n**[Templates](#templates)** |\n**[Diff](#diff)** |\n**[Dependencies](#dependencies)** |\n**[Requirements](#requirements)** |\n**[License](#license)**\n\n[![Build Status](https://travis-ci.org/cytopia/ansible-role-cloudformation.svg?branch=master)](https://travis-ci.org/cytopia/ansible-role-cloudformation)\n[![Ansible Galaxy](https://img.shields.io/ansible/role/d/23347.svg)](https://galaxy.ansible.com/cytopia/cloudformation/)\n[![Release](https://img.shields.io/github/release/cytopia/ansible-role-cloudformation.svg)](https://github.com/cytopia/ansible-role-cloudformation/releases)\n\nAnsible role to render an arbitrary number of [Jinja2](http://jinja.pocoo.org/) templates into [Cloudformation](https://aws.amazon.com/cloudformation/) files and deploy any number of stacks.\n\n\n## Motivation\n\nThis role overcomes the shortcomings of Cloudformation templates itself as well as making heavy use of Ansible's features.\n\n1. **Cloudformation limitations** - The Cloudformation syntax is very limited when it comes to programming logic such as conditions, loops and complex variables such as arrays or dictionaries. By wrapping your Cloudformation template into Ansible, you will be able to use Jinja2 directives within the Cloudformation template itself, thus having all of the beauty of Ansible and still deploy via Cloudformation stacks.\n2. **Environment agnostic** - By being able to render Cloudformation templates with custom loop variables you can finally create fully environment agnostic templates and re-use them for production, testing, staging and other environments.\n3. **Dry run** - Another advantage of using Ansible to deploy your Cloudformation templates is that Ansible supports a dry-run mode (`--check`) for Cloudformation deployments (since Ansible 2.4). During that mode it will create Change-sets and let you know **what would change** if you actually roll it out. This way you can safely test your stacks before actually applying them.\n\nThis role can be used to either only generate your templates via `cloudformation_generate_only` or also additionally deploy your rendered templates. So when you have your deployment infrastructure already in place, you can still make use of this role, by only rendering the templates and afterwards hand them over to your existing infrastructure.\n\nWhen templates are rendered, a temporary `build/` directory is created inside the role directory. This can either persist or be re-created every time this role is run. Specify the behaviour with `cloudformation_clean_build_env`.\n\n\n## Installation\n\nEither use [Ansible Galaxy](https://galaxy.ansible.com/cytopia/cloudformation/) to install the role:\n```bash\n$ ansible-galaxy install cytopia.cloudformation\n```\n\nOr git clone it into your roles directory\n```bash\n$ git clone https://github.com/cytopia/ansible-role-cloudformation /path/to/ansible/roles\n```\n\n\n## Features\n* Deploy arbitrary number of [Cloudformation](https://aws.amazon.com/cloudformation/) templates\n* Create Cloudformation templates with [Jinja2](http://jinja.pocoo.org/) templating engine\n* Render templates only and use your current infrastructure to deploy\n* Dry-run via Ansible `--check` mode which will create temporary Change sets (e.g.: lets you know if a resource requires re-creation)\n* Have line-by-line diff between local and deployed templates via [cloudformation_diff](https://github.com/cytopia/ansible-modules) module\n* Make use of [Ansible vault](https://docs.ansible.com/ansible/2.4/vault.html) to store sensitive information encrypted\n\n\n## Variables\n\n### Overview\n\nThe following variables are available in `defaults/main.yml` and can be used to setup your infrastructure.\n\n| Variable | Type | Default | Description |\n|----------|------|---------|-------------|\n| `cloudformation_clean_build_env` | bool | `False` | Clean `build/` directory of Jinja2 rendered Cloudformation templates on each run. |\n| `cloudformation_generate_only` | bool | `False` | Insteaf of deploying your Cloudformation templates, you can also only render them and have them available in the `build/` directory so you can use your current infrastructure to deploy those templates.\u003cbr/\u003e**Hint:** Specify this variable via ansible command line arguments |\n| `cloudformation_run_diff` | bool | `False` | This role ships a custom Ansible Cloudformation module **[cloudformation_diff](https://github.com/cytopia/ansible-modules)**. This module generates a text-based diff output between your local cloudformation template ready to be deployed and the currently deployed templated on AWS Cloudformation.\u003cbr/\u003eWhy would I want this?\u003cbr/\u003eThe current cloudformation module only list change sets in --check mode, which will let you know what *kind* will change (e.g. security groups), but not what exactly will change (which security groups and the values of them) In order to also be able to view the exact changes that will take place, enable the cloudformation_diff module here. |\n| `cloudformation_diff_output` | string | `json` | When `cloudformation_run_diff` is enabled, what output diff should be specified? If you write your cloudformation templates via json, use `json` here or if you write your cloudformation templates in yaml, use `yaml` here. |\n| `cloudformation_required` | list | `[]` | Array of available cloudformation stack keys that you want to enforce to be required instead of being optional. Each cloudformation stack item will be checked against the customly set required keys. In case a stack item does not contain any of those keys, an error will be thrown before any deployment has happened. |\n| `cloudformation_defaults` | dict | `{}` | Dictionary of default values to apply to every cloudformation stack. Note that those values can still be overwritten on a per stack definition. |\n| `cloudformation_stacks` | list | `[]` | Array of cloudformation stacks to deploy. |\n\n### Details\n\nThis section contains a more detailed describtion about available dict or array keys.\n\n#### `cloudformation_defaults`\n\n| Key | Type | Required | Description |\n|-----|------|----------|-------------|\n| `aws_access_key` | string | optional | AWS access key to use |\n| `aws_secret_key` | string | optional | AWS secret key to use |\n| `security_token` | string | optional | AWS security token to use |\n| `profile` | string | optional | AWS boto profile to use |\n| `notification_arns` | string | optional | Publish stack notifications to these ARN's |\n| `termination_protection` | bool | optional | Enable or disable termination protection on the stack. Only works with botocore \u003e= 1.7.18 |\n| `region` | string | optional | AWS region to deploy stack to |\n\n#### `cloudformation_stacks`\n\n| Key | Type | Required | Description |\n|-----|------|----------|-------------|\n| `stack_name` | string | required | Name of the cloudformation stack |\n| `template` | string | required | Path to the cloudformation template to render and deploy (Does not need to be rendered) |\n| `aws_access_key` | string | optional | AWS access key to use (overwrites default) |\n| `aws_secret_key` | string | optional | AWS access key to use (overwrites default)  |\n| `security_token` | string | optional | AWS security token to use (overwrites default) |\n| `profile` | string | optional | AWS boto profile to use (overwrites default) |\n| `notification_arns` | string | optional | Publish stack notifications to these ARN's (overwrites default) |\n| `termination_protection` | bool | optional | Enable or disable termination protection on the stack. Only works with botocore \u003e= 1.7.18 |\n| `region` | string | optional | AWS region to deploy stack to (overwrites default) |\n| `template_parameters` | dict | optional | Required cloudformation stack parameters |\n| `tags` | dict | optional | Tags associated with the cloudformation stack |\n\n### Examples\n\nDefine default values to be applied to all stacks (if not overwritten on a per stack definition)\n```yml\n# Enforce that 'profile' must be set for each cloudformation stack item\ncloudformation_required:\n  - profile\n\ncloudformation_defaults:\n  region: eu-central-1\n```\n\n\nDefine cloudformation stacks to be rendered and deployed\n```yml\ncloudformation_stacks:\n  - stack_name: stack-s3\n    template: files/cloudformation/s3.yml.j2\n    profile: production\n    template_parameters:\n      bucketName: my-bucket\n    tags:\n      env: production\n  - stack_name: stack-lambda\n    template: files/cloudformation/lambda.yml.j2\n    profile: production\n    termination_protection: True\n    template_parameters:\n      lambdaFunctionName: lambda\n      handler: lambda.run_handler\n      runtime: python2.7\n      s3Bucket: my-bucket\n      s3Key: lambda.py.zip\n    tags:\n      env: production\n```\n\nOnly render your Jinja2 templates, but do not deploy them to AWS. Rendered cloudformation files will be inside the `build/` directory of this role.\n```bash\n$ ansible-playbook play.yml -e cloudformation_generate_only=True\n```\n\n## Usage\n\n### Simple\n\nBasisc usage example:\n\n`playbook.yml`\n```yml\n- hosts: localhost\n  connection: local\n  roles:\n    - cloudformation\n```\n\n`group_vars/all.yml`\n```yml\n# Define Cloudformation stacks\ncloudformation_stacks:\n  # First stack\n  - stack_name: stack-s3\n    profile: testing\n    region: eu-central-1\n    template: files/cloudformation/s3.yml.j2\n    template_parameters:\n      bucketName: my-bucket\n    tags:\n      env: testing\n  # Second stack\n  - stack_name: stack-lambda\n    profile: testing\n    termination_protection: True\n    region: eu-central-1\n    template: files/cloudformation/lambda.yml.j2\n    template_parameters:\n      lambdaFunctionName: lambda\n      handler: lambda.run_handler\n      runtime: python2.7\n      s3Bucket: my-bucket\n      s3Key: lambda.py.zip\n    tags:\n      env: testing\n```\n\n### Advanced\n\nAdvanced usage example calling the role independently in different *virtual* hosts.\n\n`inventory`\n```ini\n[my-group]\ninfrastructure  ansible_connection=local\napplication     ansible_connection=local\n```\n\n`playbook.yml`\n```yml\n# Infrastructure part\n- hosts: infrastructure\n  roles:\n    - cloudformation\n  tags:\n    - infrastructure\n\n# Application part\n- hosts: application\n  roles:\n    - some-role\n  tags:\n    - some-role\n    - application\n\n- hosts: application\n  roles:\n    - cloudformation\n  tags:\n    - application\n```\n\n`group_vars/my-group.yml`\n```yml\nstack_prefix: testing\nboto_profile: testing\ns3_bucket: awesome-lambda\n\ncloudformation_defaults:\n  profile: \"{{ boto_profile }}\"\n  region: eu-central-1\n```\n\n`host_vars/infrastructure.yml`\n```yml\ncloudformation_stacks:\n  - stack_name: \"{{ stack_prefix }}-s3\"\n    template: files/cloudformation/s3.yml.j2\n    template_parameters:\n      bucketName: \"{{ s3_bucket }}\"\n    tags:\n      env: \"{{ stack_prefix }}\"\n```\n\n`host_vars/application.yml`\n```yml\ncloudformation_stacks:\n  - stack_name: \"{{ stack_prefix }}-lambda\"\n    template: files/cloudformation/lambda.yml.j2\n    template_parameters:\n      lambdaFunctionName: lambda\n      handler: lambda.run_handler\n      runtime: python2.7\n      s3Bucket: \"{{ s3_bucket }}\"\n      s3Key: lambda.py.zip\n    tags:\n      env: \"{{ stack_prefix }}\"\n```\n\n\n## Templates\n\nThis section gives a brief overview about what can be done with Cloudformation templates using Jinja2 directives.\n\n### Example: Subnet definitions\n\nThe following template can be rolled out to different staging environment and is able to include a different number of subnets.\n\nAnsible variables\n```yml\n---\n# file: staging.yml\nvpc_subnets:\n  - directive: subnetA\n    az: a\n    cidr: 10.0.10.0/24\n    tags:\n      - name: Name\n        value: staging-subnet-a\n      - name: env\n        value: staging\n  - directive: subnetB\n    az: b\n    cidr: 10.0.20.0/24\n    tags:\n      - name: Name\n        value: staging-subnet-b\n      - name: env\n        value: staging\n```\n\n```yml\n---\n# file: production.yml\nvpc_subnets:\n  - directive: subnetA\n    az: a\n    cidr: 10.0.10.0/24\n    tags:\n      - name: Name\n        value: prod-subnet-a\n      - name: env\n        value: production\n  - directive: subnetB\n    az: b\n    cidr: 10.0.20.0/24\n    tags:\n      - name: Name\n        value: prod-subnet-b\n      - name: env\n        value: production\n  - directive: subnetC\n    az: b\n    cidr: 10.0.30.0/24\n    tags:\n      - name: Name\n        value: prod-subnet-c\n      - name: env\n        value: production\n```\n\nCloudformation template\n```jinja\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: VPC Template\nResources:\n  vpc:\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: {{ vpc_cidr_block }}\n      EnableDnsSupport: true\n      EnableDnsHostnames: true\n{% if vpc_tags %}\n      Tags:\n{% for tag in vpc_tags %}\n        - Key: {{ tag.name }}\n          Value: {{ tag.value }}\n{% endfor %}\n{% endif %}\n{% for subnet in vpc_subnets %}\n  {{ subnet.directive }}:\n    Type: AWS::EC2::Subnet\n    Properties:\n      AvailabilityZone: {{ subnet.az }}\n      CidrBlock: {{ subnet.cidr }}\n      VpcId: !Ref vpc\n{% if subnet.tags %}\n      Tags:\n{% for tag in subnet.tags %}\n        - Key: {{ tag.name }}\n          Value: {{ tag.value }}\n{% endfor %}\n{% endif %}\n```\n\n\n### Example: Security groups\n\nDefining security groups with IP-specific rules is very difficult when you want to keep environment agnosticity and still use the same Cloudformation template for all environments. This however can easily be overcome by providing environment specific array definitions via Jinja2.\n\nAnsible variables\n```yml\n---\n# file: staging.yml\n# Staging is wiede open, so that developers are able to\n# connect from attached VPN's\nsecurity_groups:\n  - protocol:  tcp\n    from_port: 3306\n    to_port:   3306\n    cidr_ip:   10.0.0.1/32\n  - protocol:  tcp\n    from_port: 3306\n    to_port:   3306\n    cidr_ip:   192.168.0.15/32\n  - protocol:  tcp\n    from_port: 3306\n    to_port:   3306\n    cidr_ip:   172.16.0.0/16\n```\n\n```yml\n---\n# file: production.yml\n# The production environment has far less rules as well as other\n# ip ranges.\nsecurity_groups:\n  - protocol:  tcp\n    from_port: 3306\n    to_port:   3306\n    cidr_ip:   10.0.15.1/32\n```\n\nCloudformation template\n```jinja\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: VPC Template\nResources:\n  rdsSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: RDS security group\n{% if security_groups %}\n      SecurityGroupIngress:\n{% for rule in security_groups %}\n        - IpProtocol: \"{{ rule.protocol }}\"\n          FromPort: \"{{ rule.from_port }}\"\n          ToPort: \"{{ rule.to_port }}\"\n          CidrIp: \"{{ rule.cidr_ip }}\"\n{% endfor %}\n{% endif %}\n```\n\n## Diff\n\nWhen having enable `cloudformation_run_diff`, you will be able to see line by line diff output from you local (jinja2 rendered) template against the one which is currently deployed on AWS. To give you an impression about how this looks, see the following example output:\n\nMake sure to run Ansible with `--diff` to make it work:\n```bash\n$ ansible-playbook play.yml --diff\n```\n\n### Json diff\nTo have it output in json diff mode, set `cloudformation_diff_output` to `json`.\n```diff\nTASK [cloudformation : diff cloudformation template file] *********************************************\n--- before\n+++ after\n@@ -38,7 +38,6 @@\n             \"Type\": \"AWS::S3::BucketPolicy\"\n         },\n         \"s3Bucket\": {\n-            \"DeletionPolicy\": \"Retain\",\n             \"Properties\": {\n                 \"BucketName\": {\n                     \"Ref\": \"bucketName\"\n```\n\n### Yaml diff\nTo have it output in yaml diff mode, set `cloudformation_diff_output` to `yaml`.\n```diff\nTASK [cloudformation : diff cloudformation template file] *********************************************\n--- before\n+++ after\n@@ -14,7 +14,6 @@\n               Service: !Sub 'logs.${AWS::Region}.amazonaws.com'\n       Bucket: !Ref 's3Bucket'\n   s3Bucket:\n-    DeletionPolicy: Retain\n     Type: AWS::S3::Bucket\n     Properties:\n       BucketName: !Ref 'bucketName'\n```\n\n\n## Dependencies\n\nThis role does not depend on any other roles.\n\n\n## Requirements\n\nUse at least **Ansible 2.5** in order to also have `--check` mode for cloudformation.\n\nThe python module `cfn_flip` is required, when using line-by-line diff of local and remote Cloudformation templates (`cloudformation_run_diff=True`). This can easily be installed locally:\n```bash\n$ pip install cfn_flip\n```\n\n\n## License\n\n[MIT License](LICENSE.md)\n\nCopyright (c) 2017 [cytopia](https://github.com/cytopia)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcytopia%2Fansible-role-cloudformation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcytopia%2Fansible-role-cloudformation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcytopia%2Fansible-role-cloudformation/lists"}