cytopia.cloudformation

Ansible 角色:创建云形成堆栈

动机 | 安装 | 特点 | 变量 | 使用 | 模板 | 差异 | 依赖 | 要求 | 许可证

构建状态 Ansible Galaxy 发布

Ansible 角色用于将任意数量的 Jinja2 模板渲染成 CloudFormation 文件,并部署任意数量的堆栈。

动机

该角色克服了 CloudFormation 模板本身的局限性,并充分利用了 Ansible 的特性。

  1. CloudFormation 限制 - CloudFormation 的语法在条件、循环和复杂变量(如数组或字典)等编程逻辑方面非常有限。通过将 CloudFormation 模板包装到 Ansible 中,您将能够在模板中使用 Jinja2 指令,从而结合 Ansible 的优雅并通过 CloudFormation 堆栈进行部署。
  2. 环境无关 - 通过能够使用自定义循环变量渲染 CloudFormation 模板,您可以创建完全环境无关的模板,并将其重用于生产、测试、临时和其他环境。
  3. 干运行 - 使用 Ansible 部署 CloudFormation 模板的另一个优点是 Ansible 支持干运行模式(--check)以进行 CloudFormation 部署(自 Ansible 2.4 起)。在该模式下,它会创建变更集并告诉您如果您实际推出,会发生什么变化。这样,您可以安全地测试堆栈,然后再实际应用它们。

该角色可以仅通过 cloudformation_generate_only 生成模板,或者额外部署渲染的模板。因此,当您已经设置好部署基础设施时,可以通过仅渲染模板并将其交给现有基础设施来继续使用该角色。

当模板被渲染时,角色目录中会创建一个临时的 build/ 目录。此目录可以每次运行该角色时保持不变或重新创建。用 cloudformation_clean_build_env 指定行为。

安装

您可以使用 Ansible Galaxy 安装该角色:

$ ansible-galaxy install cytopia.cloudformation

或者将其克隆到您的角色目录中:

$ git clone https://github.com/cytopia/ansible-role-cloudformation /path/to/ansible/roles

特点

  • 部署任意数量的 CloudFormation 模板
  • 使用 Jinja2 模板引擎创建 CloudFormation 模板
  • 仅渲染模板,并使用当前基础设施进行部署
  • 通过 Ansible --check 模式进行干运行,这将创建临时变更集(例如:告知您某个资源是否需要重新创建)
  • 通过 cloudformation_diff 模块在本地和已部署的模板之间提供逐行差异
  • 使用 Ansible vault 存储加密的敏感信息

变量

概述

以下变量可在 defaults/main.yml 中使用,以设置您的基础设施。

变量 类型 默认值 描述
cloudformation_clean_build_env boolean False 每次运行时清理 Jinja2 渲染的 CloudFormation 模板的 build/ 目录。
cloudformation_generate_only boolean False 与部署 CloudFormation 模板相反,您也可以仅渲染它们并在 build/ 目录中使用,以便使用您当前的基础设施进行部署。
提示:通过 ansible 命令行参数指定此变量
cloudformation_run_diff boolean False 此角色提供了一个自定义的 Ansible CloudFormation 模块 **cloudformation_diff**。此模块生成本地可以部署的 CloudFormation 模板与当前在 AWS CloudFormation 上部署的模板之间的文本差异输出。
我为什么要这个?
当前的 CloudFormation 模块在 --check 模式下只列出变更集,这将让您知道将会发生什么 类型 的变化(例如安全组),但不知道具体会发生什么变化(哪个安全组及其值)。为了能够查看将要发生的确切变化,请在此处启用 cloudformation_diff 模块。
cloudformation_diff_output 字符串 json 当启用 cloudformation_run_diff 时,应该指定什么样的输出差异?如果您使用 JSON 编写 CloudFormation 模板,则在此处使用 json,如果使用 YAML 编写,则在此处使用 yaml
cloudformation_required 数组 [] 可用 CloudFormation 堆栈键的数组,您希望强制它们为必需,而不是可选。将检查每个 CloudFormation 堆栈项是否符合自定义设置的所需键。如果堆栈项不包含其中的任何键,则在实际部署之前将抛出错误。
cloudformation_defaults 字典 {} 要应用于每个 CloudFormation 堆栈的默认值字典。请注意,这些值仍然可以在每个堆栈定义中覆盖。
cloudformation_stacks 数组 [] 要部署的 CloudFormation 堆栈的数组。

详细信息

本节包含有关可用的字典或数组键的更详细描述。

cloudformation_defaults

类型 必需 描述
aws_access_key 字符串 可选 要使用的 AWS 访问密钥
aws_secret_key 字符串 可选 要使用的 AWS 秘密密钥
security_token 字符串 可选 要使用的 AWS 安全令牌
profile 字符串 可选 要使用的 AWS boto 配置文件
notification_arns 字符串 可选 将堆栈通知发布到这些 ARN
termination_protection boolean 可选 启用或禁用堆栈的终止保护。仅适用于 botocore >= 1.7.18
region 字符串 可选 部署堆栈的 AWS 区域

cloudformation_stacks

类型 必需 描述
stack_name 字符串 必需 CloudFormation 堆栈的名称
template 字符串 必需 要渲染和部署的 CloudFormation 模板的路径(无需渲染)
aws_access_key 字符串 可选 要使用的 AWS 访问密钥(覆盖默认值)
aws_secret_key 字符串 可选 要使用的 AWS 秘密密钥(覆盖默认值)
security_token 字符串 可选 要使用的 AWS 安全令牌(覆盖默认值)
profile 字符串 可选 要使用的 AWS boto 配置文件(覆盖默认值)
notification_arns 字符串 可选 将堆栈通知发布到这些 ARN(覆盖默认值)
termination_protection boolean 可选 启用或禁用堆栈的终止保护。仅适用于 botocore >= 1.7.18
region 字符串 可选 部署堆栈到的 AWS 区域(覆盖默认值)
template_parameters 字典 可选 必需的 CloudFormation 堆栈参数
tags 字典 可选 与 CloudFormation 堆栈关联的标签

示例

定义要应用于所有堆栈的默认值(如果未在每个堆栈定义中覆盖)

# 强制每个 CloudFormation 堆栈项必须设置 'profile'
cloudformation_required:
  - profile

cloudformation_defaults:
  region: eu-central-1

定义要渲染和部署的 CloudFormation 堆栈

cloudformation_stacks:
  - stack_name: stack-s3
    template: files/cloudformation/s3.yml.j2
    profile: production
    template_parameters:
      bucketName: my-bucket
    tags:
      env: production
  - stack_name: stack-lambda
    template: files/cloudformation/lambda.yml.j2
    profile: production
    termination_protection: True
    template_parameters:
      lambdaFunctionName: lambda
      handler: lambda.run_handler
      runtime: python2.7
      s3Bucket: my-bucket
      s3Key: lambda.py.zip
    tags:
      env: production

仅渲染您的 Jinja2 模板,但不将其部署到 AWS。渲染的 CloudFormation 文件将位于该角色的 build/ 目录中。

$ ansible-playbook play.yml -e cloudformation_generate_only=True

使用

简单

基础用法示例:

playbook.yml

- hosts: localhost
  connection: local
  roles:
    - cloudformation

group_vars/all.yml

# 定义 CloudFormation 堆栈
cloudformation_stacks:
  # 第一个堆栈
  - stack_name: stack-s3
    profile: testing
    region: eu-central-1
    template: files/cloudformation/s3.yml.j2
    template_parameters:
      bucketName: my-bucket
    tags:
      env: testing
  # 第二个堆栈
  - stack_name: stack-lambda
    profile: testing
    termination_protection: True
    region: eu-central-1
    template: files/cloudformation/lambda.yml.j2
    template_parameters:
      lambdaFunctionName: lambda
      handler: lambda.run_handler
      runtime: python2.7
      s3Bucket: my-bucket
      s3Key: lambda.py.zip
    tags:
      env: testing

高级

高级用法示例,在不同的 虚拟 主机中独立调用角色。

inventory

[my-group]
infrastructure  ansible_connection=local
application     ansible_connection=local

playbook.yml

# 基础设施部分
- hosts: infrastructure
  roles:
    - cloudformation
  tags:
    - infrastructure

# 应用程序部分
- hosts: application
  roles:
    - some-role
  tags:
    - some-role
    - application

- hosts: application
  roles:
    - cloudformation
  tags:
    - application

group_vars/my-group.yml

stack_prefix: testing
boto_profile: testing
s3_bucket: awesome-lambda

cloudformation_defaults:
  profile: "{{ boto_profile }}"
  region: eu-central-1

host_vars/infrastructure.yml

cloudformation_stacks:
  - stack_name: "{{ stack_prefix }}-s3"
    template: files/cloudformation/s3.yml.j2
    template_parameters:
      bucketName: "{{ s3_bucket }}"
    tags:
      env: "{{ stack_prefix }}"

host_vars/application.yml

cloudformation_stacks:
  - stack_name: "{{ stack_prefix }}-lambda"
    template: files/cloudformation/lambda.yml.j2
    template_parameters:
      lambdaFunctionName: lambda
      handler: lambda.run_handler
      runtime: python2.7
      s3Bucket: "{{ s3_bucket }}"
      s3Key: lambda.py.zip
    tags:
      env: "{{ stack_prefix }}"

模板

本节提供了使用 Jinja2 指令对 CloudFormation 模板可以做的事情的简要概述。

示例:子网定义

以下模板可以推广到不同的临时环境,并能够包含不同数量的子网。

Ansible 变量

---
# 文件:staging.yml
vpc_subnets:
  - directive: subnetA
    az: a
    cidr: 10.0.10.0/24
    tags:
      - name: Name
        value: staging-subnet-a
      - name: env
        value: staging
  - directive: subnetB
    az: b
    cidr: 10.0.20.0/24
    tags:
      - name: Name
        value: staging-subnet-b
      - name: env
        value: staging
---
# 文件:production.yml
vpc_subnets:
  - directive: subnetA
    az: a
    cidr: 10.0.10.0/24
    tags:
      - name: Name
        value: prod-subnet-a
      - name: env
        value: production
  - directive: subnetB
    az: b
    cidr: 10.0.20.0/24
    tags:
      - name: Name
        value: prod-subnet-b
      - name: env
        value: production
  - directive: subnetC
    az: b
    cidr: 10.0.30.0/24
    tags:
      - name: Name
        value: prod-subnet-c
      - name: env
        value: production

CloudFormation 模板

AWSTemplateFormatVersion: '2010-09-09'
Description: VPC 模板
Resources:
  vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: {{ vpc_cidr_block }}
      EnableDnsSupport: true
      EnableDnsHostnames: true
{% if vpc_tags %}
      Tags:
{% for tag in vpc_tags %}
        - Key: {{ tag.name }}
          Value: {{ tag.value }}
{% endfor %}
{% endif %}
{% for subnet in vpc_subnets %}
  {{ subnet.directive }}:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: {{ subnet.az }}
      CidrBlock: {{ subnet.cidr }}
      VpcId: !Ref vpc
{% if subnet.tags %}
      Tags:
{% for tag in subnet.tags %}
        - Key: {{ tag.name }}
          Value: {{ tag.value }}
{% endfor %}
{% endif %}

示例:安全组

使用特定 IP 规则定义安全组时,如果想要保持环境无关并使用相同的 CloudFormation 模板来管理所有环境,会非常困难。然而,可以通过提供环境特定的数组定义通过 Jinja2 来轻松克服这一点。

Ansible 变量

---
# 文件:staging.yml
# 临时环境开放,以便开发人员能够
# 从连接的 VPN 中进行连接
security_groups:
  - protocol:  tcp
    from_port: 3306
    to_port:   3306
    cidr_ip:   10.0.0.1/32
  - protocol:  tcp
    from_port: 3306
    to_port:   3306
    cidr_ip:   192.168.0.15/32
  - protocol:  tcp
    from_port: 3306
    to_port:   3306
    cidr_ip:   172.16.0.0/16
---
# 文件:production.yml
# 生产环境的规则远少于其他
# IP 范围。
security_groups:
  - protocol:  tcp
    from_port: 3306
    to_port:   3306
    cidr_ip:   10.0.15.1/32

CloudFormation 模板

AWSTemplateFormatVersion: '2010-09-09'
Description: VPC 模板
Resources:
  rdsSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: RDS 安全组
{% if security_groups %}
      SecurityGroupIngress:
{% for rule in security_groups %}
        - IpProtocol: "{{ rule.protocol }}"
          FromPort: "{{ rule.from_port }}"
          ToPort: "{{ rule.to_port }}"
          CidrIp: "{{ rule.cidr_ip }}"
{% endfor %}
{% endif %}

差异

当启用 cloudformation_run_diff 时,您将能够查看本地(渲染的 jinja2)模板与当前在 AWS 上部署的模板之间的逐行差异输出。为了给您这个效果,您可以查看以下示例输出:

确保使用 --diff 运行 Ansible 以使其生效:

$ ansible-playbook play.yml --diff

JSON 差异

要以 JSON 差异模式输出,请将 cloudformation_diff_output 设置为 json

TASK [cloudformation : diff cloudformation template file] *********************************************
--- before
+++ after
@@ -38,7 +38,6 @@
             "Type": "AWS::S3::BucketPolicy"
         },
         "s3Bucket": {
-            "DeletionPolicy": "Retain",
             "Properties": {
                 "BucketName": {
                     "Ref": "bucketName"

YAML 差异

要以 YAML 差异模式输出,请将 cloudformation_diff_output 设置为 yaml

TASK [cloudformation : diff cloudformation template file] *********************************************
--- before
+++ after
@@ -14,7 +14,6 @@
               Service: !Sub 'logs.${AWS::Region}.amazonaws.com'
       Bucket: !Ref 's3Bucket'
   s3Bucket:
-    DeletionPolicy: Retain
     Type: AWS::S3::Bucket
     Properties:
       BucketName: !Ref 'bucketName'

依赖

此角色不依赖于其他角色。

要求

使用至少 Ansible 2.5,以便也有适用于 CloudFormation 的 --check 模式。

当使用本地和远程 CloudFormation 模板的逐行差异时(cloudformation_run_diff=True),需要 Python 模块 cfn_flip。这可以轻松地在本地安装:

$ pip install cfn_flip

许可证

MIT 许可证

版权所有 (c) 2017 cytopia

关于项目

Ansible role to render an arbitrary number of Jinja2 templates into cloudformation files and create any number of stacks.

安装
ansible-galaxy install cytopia.cloudformation
许可证
mit
下载
5.2k
拥有者
DevOps Engineer