cytopia.cloudformation
Rol de Ansible: Crear pilas de CloudFormation
Motivación | Instalación | Características | Variables | Uso | Plantillas | Diferencia | Dependencias | Requisitos | Licencia
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.
- 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.
- 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.
- 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
Copyright (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