cytopia.cloudformation

Rol de Ansible: Crear pilas de CloudFormation

Motivación | Instalación | Características | Variables | Uso | Plantillas | Diferencia | Dependencias | Requisitos | Licencia

Estado de Construcción Ansible Galaxy Liberación

Rol de Ansible para renderizar un número arbitrario de plantillas de Jinja2 en archivos de CloudFormation y desplegar cualquier número de pilas.

Motivación

Este rol supera las limitaciones de las plantillas de CloudFormation y aprovecha en gran medida las características de Ansible.

  1. Limitaciones de CloudFormation - La sintaxis de CloudFormation es muy limitada en cuanto a lógica de programación, como condiciones, bucles y variables complejas como arreglos o diccionarios. Al envolver tu plantilla de CloudFormation en Ansible, podrás usar directivas de Jinja2 dentro de la propia plantilla de CloudFormation, obteniendo así toda la belleza de Ansible y aún así desplegar mediante pilas de CloudFormation.
  2. Independiente del entorno - Al poder renderizar plantillas de CloudFormation con variables de bucle personalizadas, finalmente podrás crear plantillas completamente independientes del entorno y reutilizarlas en producción, pruebas, preproducción y otros entornos.
  3. Ejecutar en modo seco - Otra ventaja de usar Ansible para desplegar tus plantillas de CloudFormation es que Ansible admite un modo de ejecución en seco (--check) para despliegues de CloudFormation (desde Ansible 2.4). Durante ese modo, creará conjuntos de cambios y te informará lo que cambiaría si realmente lo aplicas. De esta manera, puedes probar tus pilas de forma segura antes de aplicarlas realmente.

Este rol se puede utilizar para solo generar tus plantillas a través de cloudformation_generate_only o también desplegar tus plantillas renderizadas. Así que cuando ya tienes tu infraestructura de despliegue en su lugar, todavía puedes usar este rol, solo renderizando las plantillas y luego entregándolas a tu infraestructura existente.

Cuando se renderizan las plantillas, se crea un directorio temporal build/ dentro del directorio del rol. Este puede persistir o recrearse cada vez que se ejecute este rol. Especifica el comportamiento con cloudformation_clean_build_env.

Instalación

Puedes usar Ansible Galaxy para instalar el rol:

$ ansible-galaxy install cytopia.cloudformation

O clona el repositorio en tu directorio de roles:

$ git clone https://github.com/cytopia/ansible-role-cloudformation /ruta/al/directorio/de/roles

Características

  • Desplegar un número arbitrario de plantillas de CloudFormation
  • Crear plantillas de CloudFormation con el motor de plantillas Jinja2
  • Renderizar solo las plantillas y usar tu infraestructura actual para desplegar
  • Ejecución en seco mediante el modo --check de Ansible, que creará conjuntos de cambios temporales (por ejemplo, te informa si un recurso requiere ser recreado)
  • Tener una diferencia línea por línea entre las plantillas locales y las desplegadas mediante el módulo cloudformation_diff
  • Usar Ansible Vault para almacenar información sensible de forma encriptada

Variables

Descripción general

Las siguientes variables están disponibles en defaults/main.yml y se pueden usar para configurar tu infraestructura.

Variable Tipo Predeterminado Descripción
cloudformation_clean_build_env bool False Limpiar el directorio build/ de las plantillas de CloudFormation renderizadas de Jinja2 en cada ejecución.
cloudformation_generate_only bool False En lugar de desplegar tus plantillas de CloudFormation, puedes solo renderizarlas y tenerlas disponibles en el directorio build/ para usar tu infraestructura actual para desplegar esas plantillas.
Sugerencia: Especifica esta variable a través de argumentos de línea de comando de ansible
cloudformation_run_diff bool False Este rol incluye un módulo de CloudFormation personalizado de Ansible cloudformation_diff. Este módulo genera una salida de diferencia basada en texto entre tu plantilla de CloudFormation local lista para ser desplegada y la plantilla actualmente desplegada en AWS CloudFormation.
¿Por qué querría esto?
El módulo actual de cloudformation solo lista conjuntos de cambios en modo --check, lo que te permitirá saber qué tipo cambiará (por ejemplo, grupos de seguridad), pero no qué exactamente cambiará (qué grupos de seguridad y los valores de ellos). Para poder ver los cambios exactos que ocurrirán, habilita el módulo cloudformation_diff aquí.
cloudformation_diff_output string json Cuando cloudformation_run_diff está habilitado, ¿qué salida de diferencia debería especificarse? Si escribes tus plantillas de cloudformation en json, usa json aquí o si las escribes en yaml, usa yaml aquí.
cloudformation_required list [] Array de claves de pila de cloudformation disponibles que deseas forzar a ser requeridas en lugar de opcionales. Cada elemento de la pila de cloudformation se comprobará contra las claves requeridas establecidas de forma personalizada. En caso de que un elemento de la pila no contenga ninguna de esas claves, se lanzará un error antes de que se produzca cualquier despliegue.
cloudformation_defaults dict {} Diccionario de valores predeterminados que se aplican a cada pila de cloudformation. Ten en cuenta que esos valores aún pueden ser sobrescritos en la definición de cada pila.
cloudformation_stacks list [] Array de pilas de cloudformation a desplegar.

Detalles

Esta sección contiene una descripción más detallada sobre las claves disponibles en diccionarios o arreglos.

cloudformation_defaults

Clave Tipo Requerido Descripción
aws_access_key string opcional Clave de acceso de AWS a usar
aws_secret_key string opcional Clave secreta de AWS a usar
security_token string opcional Token de seguridad de AWS a usar
profile string opcional Perfil de boto de AWS a usar
notification_arns string opcional Publicar notificaciones de pila a estos ARN's
termination_protection bool opcional Habilitar o deshabilitar la protección contra eliminación en la pila. Solo funciona con botocore >= 1.7.18
region string opcional Región de AWS para desplegar la pila

cloudformation_stacks

Clave Tipo Requerido Descripción
stack_name string requerido Nombre de la pila de cloudformation
template string requerido Ruta a la plantilla de cloudformation para renderizar y desplegar (no necesita ser renderizada)
aws_access_key string opcional Clave de acceso de AWS a usar (sobrescribe el predeterminado)
aws_secret_key string opcional Clave de acceso de AWS a usar (sobrescribe el predeterminado)
security_token string opcional Token de seguridad de AWS a usar (sobrescribe el predeterminado)
profile string opcional Perfil de boto de AWS a usar (sobrescribe el predeterminado)
notification_arns string opcional Publicar notificaciones de pila a estos ARN's (sobrescribe el predeterminado)
termination_protection bool opcional Habilitar o deshabilitar la protección contra eliminación en la pila. Solo funciona con botocore >= 1.7.18
region string opcional Región de AWS para desplegar la pila (sobrescribe el predeterminado)
template_parameters dict opcional Parámetros de pila de cloudformation requeridos
tags dict opcional Etiquetas asociadas con la pila de cloudformation

Ejemplos

Definir valores predeterminados que se apliquen a todas las pilas (si no se sobrescriben en la definición de cada pila)

# Forzar que 'profile' debe estar establecido para cada elemento de la pila de cloudformation
cloudformation_required:
  - profile

cloudformation_defaults:
  region: eu-central-1

Definir pilas de cloudformation que se renderizarán y desplegarán

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

Solo renderizar tus plantillas de Jinja2, pero no desplegarlas en AWS. Los archivos de CloudFormation renderizados estarán dentro del directorio build/ de este rol.

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

Uso

Simple

Ejemplo básico de uso:

playbook.yml

- hosts: localhost
  connection: local
  roles:
    - cloudformation

group_vars/all.yml

# Definir pilas de CloudFormation
cloudformation_stacks:
  # Primera pila
  - stack_name: stack-s3
    profile: testing
    region: eu-central-1
    template: files/cloudformation/s3.yml.j2
    template_parameters:
      bucketName: my-bucket
    tags:
      env: testing
  # Segunda pila
  - 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

Avanzado

Ejemplo de uso avanzado llamando al rol independientemente en diferentes hosts virtuales.

inventory

[mi-grupo]
infraestructura  ansible_connection=local
aplicacion       ansible_connection=local

playbook.yml

# Parte de infraestructura
- hosts: infraestructura
  roles:
    - cloudformation
  tags:
    - infraestructura

# Parte de la aplicación
- hosts: aplicacion
  roles:
    - some-role
  tags:
    - some-role
    - aplicacion

- hosts: aplicacion
  roles:
    - cloudformation
  tags:
    - aplicacion

group_vars/mi-grupo.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 }}"

Plantillas

Esta sección ofrece una breve visión sobre lo que se puede hacer con las plantillas de CloudFormation usando directivas de Jinja2.

Ejemplo: Definiciones de subred

La siguiente plantilla se puede implementar en diferentes entornos de preproducción y es capaz de incluir un número diferente de subredes.

Variables de Ansible

---
# archivo: 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
---
# archivo: 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

Plantilla de CloudFormation

AWSTemplateFormatVersion: '2010-09-09'
Description: Plantilla 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 %}
{% endfor %}

Ejemplo: Grupos de seguridad

Definir grupos de seguridad con reglas específicas de IP es muy difícil cuando se quiere mantener la independencia del entorno y usar la misma plantilla de CloudFormation para todos los ambientes. Sin embargo, esto se puede superar fácilmente proporcionando definiciones de arrays específicas del entorno mediante Jinja2.

Variables de Ansible

---
# archivo: staging.yml
# Staging está abierto para que los desarrolladores puedan
# conectarse desde las VPN adjuntas
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
---
# archivo: production.yml
# El entorno de producción tiene muchas menos reglas y
# otros rangos de ip.
security_groups:
  - protocol:  tcp
    from_port: 3306
    to_port:   3306
    cidr_ip:   10.0.15.1/32

Plantilla de CloudFormation

AWSTemplateFormatVersion: '2010-09-09'
Description: Plantilla VPC
Resources:
  rdsSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Grupo de seguridad de 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 %}

Diferencia

Al habilitar cloudformation_run_diff, podrás ver la salida de diferencias línea por línea entre tu plantilla local (renderizada con Jinja2) y la que actualmente está desplegada en AWS. Para darte una idea de cómo luce, aquí tienes un ejemplo de salida:

Asegúrate de ejecutar Ansible con --diff para que funcione:

$ ansible-playbook play.yml --diff

Diferencia en Json

Para tener la salida en modo de diferencia json, establece cloudformation_diff_output en json.

TASK [cloudformation : diferencia en la plantilla de cloudformation] *********************************************
--- antes
+++ después
@@ -38,7 +38,6 @@
             "Type": "AWS::S3::BucketPolicy"
         },
         "s3Bucket": {
-            "DeletionPolicy": "Retain",
             "Properties": {
                 "BucketName": {
                     "Ref": "bucketName"

Diferencia en Yaml

Para tener la salida en modo de diferencia yaml, establece cloudformation_diff_output en yaml.

TASK [cloudformation : diferencia en la plantilla de cloudformation] *********************************************
--- antes
+++ después
@@ -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'

Dependencias

Este rol no depende de ningún otro rol.

Requisitos

Usar al menos Ansible 2.5 para tener también el modo --check para cloudformation.

El módulo de python cfn_flip es requerido cuando se usa la diferencia línea por línea de plantillas de CloudFormation locales y remotas (cloudformation_run_diff=True). Esto se puede instalar fácilmente localmente:

$ pip install cfn_flip

Licencia

Licencia MIT

Copyright (c) 2017 cytopia

Acerca del proyecto

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

Instalar
ansible-galaxy install cytopia.cloudformation
Licencia
mit
Descargas
5.2k
Propietario
DevOps Engineer