cytopia.cloudformation
Ansible-Rolle: Erstelle Cloudformation-Stacks
Motivation | Installation | Funktionen | Variablen | Verwendung | Vorlagen | Diff | Abhängigkeiten | Anforderungen | Lizenz
Ansible-Rolle, um eine beliebige Anzahl von Jinja2-Vorlagen in Cloudformation Dateien zu rendern und jede Anzahl von Stacks bereitzustellen.
Motivation
Diese Rolle überwindet die Einschränkungen der Cloudformation-Vorlagen selbst und nutzt intensiv die Funktionen von Ansible.
- Cloudformation-Beschränkungen - Die Syntax von Cloudformation ist sehr begrenzt, wenn es um Programmlogik wie Bedingungen, Schleifen und komplexe Variablen wie Arrays oder Dictionaries geht. Durch das Einwickeln deiner Cloudformation-Vorlage in Ansible kannst du Jinja2-Direktiven innerhalb der Cloudformation-Vorlage selbst verwenden und hast so alle Vorteile von Ansible und kannst dennoch über Cloudformation-Stacks bereitstellen.
- Umgebungsunabhängig - Durch die Möglichkeit, Cloudformation-Vorlagen mit benutzerdefinierten Schleifenvariablen zu rendern, kannst du schließlich vollständig umgebungsunabhängige Vorlagen erstellen und sie für Produktion, Tests, Staging und andere Umgebungen wiederverwenden.
- Probelauf - Ein weiterer Vorteil der Verwendung von Ansible zur Bereitstellung deiner Cloudformation-Vorlagen ist, dass Ansible einen Probelaufmodus (
--check
) für Cloudformation-Bereitstellungen unterstützt (seit Ansible 2.4). In diesem Modus werden Change-Sets erstellt und du wirst informiert, was sich ändern würde, wenn du es tatsächlich ausrollst. So kannst du deine Stacks sicher testen, bevor du sie anwendest.
Diese Rolle kann entweder nur dazu verwendet werden, deine Vorlagen über cloudformation_generate_only
zu generieren oder sie zusätzlich auch zu deployen. Wenn du deine Bereitstellungsinfrastruktur bereits eingerichtet hast, kannst du diese Rolle weiterhin nutzen, indem du nur die Vorlagen renderst und sie anschließend deiner bestehenden Infrastruktur übergibst.
Wenn die Vorlagen gerendert werden, wird ein temporäres build/
-Verzeichnis im Rollenverzeichnis erstellt. Dieses kann entweder bestehen bleiben oder bei jedem Lauf dieser Rolle neu erstellt werden. Gib das Verhalten mit cloudformation_clean_build_env
an.
Installation
Du kannst Ansible Galaxy verwenden, um die Rolle zu installieren:
$ ansible-galaxy install cytopia.cloudformation
Oder klone sie in dein Rollenverzeichnis:
$ git clone https://github.com/cytopia/ansible-role-cloudformation /path/to/ansible/roles
Funktionen
- Bereitstellung beliebiger Anzahl von Cloudformation Vorlagen
- Erstelle Cloudformation-Vorlagen mit der Jinja2 Template-Engine
- Rendern der Vorlagen nur und verwende deine aktuelle Infrastruktur zur Bereitstellung
- Probelauf über Ansible im
--check
Modus, der temporäre Change-Sets erstellt (z.B. wird angezeigt, ob eine Ressource neu erstellt werden muss) - Zeige zeilenweise Unterschiede zwischen lokalen und bereitgestellten Vorlagen über das cloudformation_diff Modul an
- Nutze das Ansible vault, um sensible Informationen verschlüsselt zu speichern
Variablen
Übersicht
Die folgenden Variablen sind in defaults/main.yml
verfügbar und können zur Einrichtung deiner Infrastruktur verwendet werden.
Variable | Typ | Standard | Beschreibung |
---|---|---|---|
cloudformation_clean_build_env |
bool | False |
Lösche das build/ Verzeichnis der Jinja2 gerenderten Cloudformation-Vorlagen bei jedem Lauf. |
cloudformation_generate_only |
bool | False |
Anstelle der Bereitstellung deiner Cloudformation-Vorlagen kannst du diese nur rendern und im build/ Verzeichnis verfügbar machen, sodass du deine aktuelle Infrastruktur nutzen kannst, um diese Vorlagen bereitzustellen.Hinweis: Gib diese Variable über die Ansible-Befehlszeilenargumente an. |
cloudformation_run_diff |
bool | False |
Diese Rolle enthält ein benutzerdefiniertes Ansible Cloudformation Modul cloudformation_diff. Dieses Modul erzeugt eine textbasierte Diff-Ausgabe zwischen deiner lokalen Cloudformation-Vorlage, die bereitgestellt werden soll, und der aktuell auf AWS Cloudformation bereitgestellten Vorlage. Warum ist das wichtig? Das aktuelle Cloudformation-Modul listet nur Change-Sets im --check-Modus auf, was dir mitteilt, was sich ändern wird (z.B. Sicherheitsgruppen), aber nicht, was genau sich ändern wird (welche Sicherheitsgruppen und deren Werte). Um auch die genauen Änderungen zu sehen, die stattfinden werden, aktiviere hier das cloudformation_diff Modul. |
cloudformation_diff_output |
string | json |
Wenn cloudformation_run_diff aktiviert ist, welches Ausgabe-Diff soll angegeben werden? Wenn du deine Cloudformation-Vorlagen in json schreibst, verwende hier json , oder wenn du sie in yaml schreibst, verwende hier yaml . |
cloudformation_required |
list | [] |
Array von verfügbaren Cloudformation-Stack-Schlüsseln, die du als erforderlich festlegen möchtest, anstatt optional zu sein. Jedes Cloudformation-Stack-Element wird gegen die benutzerdefiniert festgelegten erforderlichen Schlüssel überprüft. Falls ein Stack-Element keinen dieser Schlüssel enthält, wird ein Fehler ausgegeben, bevor eine Bereitstellung erfolgt. |
cloudformation_defaults |
dict | {} |
Wörterbuch von Standardwerten, die auf jeden Cloudformation-Stack angewendet werden. Beachte, dass diese Werte auch pro Stack-Definition überschrieben werden können. |
cloudformation_stacks |
list | [] |
Array von Cloudformation-Stacks, die bereitgestellt werden sollen. |
Details
Dieser Abschnitt enthält eine detailliertere Beschreibung der verfügbaren Dict- oder Array-Schlüssel.
cloudformation_defaults
Schlüssel | Typ | Erforderlich | Beschreibung |
---|---|---|---|
aws_access_key |
string | optional | Zu verwendender AWS-Zugangsschlüssel |
aws_secret_key |
string | optional | Zu verwendender AWS-Geheimschlüssel |
security_token |
string | optional | Zu verwendender AWS-Sicherheitstoken |
profile |
string | optional | Zu verwendendes AWS-Boto-Profil |
notification_arns |
string | optional | Sendet Stack-Benachrichtigungen an diese ARN's |
termination_protection |
bool | optional | Aktiviert oder deaktiviert den Kündigungsschutz des Stacks. Funktioniert nur mit Botocore >= 1.7.18 |
region |
string | optional | AWS-Region, in der der Stack bereitgestellt werden soll |
cloudformation_stacks
Schlüssel | Typ | Erforderlich | Beschreibung |
---|---|---|---|
stack_name |
string | erforderlich | Name des Cloudformation-Stacks |
template |
string | erforderlich | Pfad zur Cloudformation-Vorlage, die gerendert und bereitgestellt werden soll (muss nicht gerendert werden) |
aws_access_key |
string | optional | Zu verwendender AWS-Zugangsschlüssel (überschreibt den Standard) |
aws_secret_key |
string | optional | Zu verwendender AWS-Geheimschlüssel (überschreibt den Standard) |
security_token |
string | optional | Zu verwendender AWS-Sicherheitstoken (überschreibt den Standard) |
profile |
string | optional | Zu verwendendes AWS-Boto-Profil (überschreibt den Standard) |
notification_arns |
string | optional | Sendet Stack-Benachrichtigungen an diese ARN's (überschreibt den Standard) |
termination_protection |
bool | optional | Aktiviert oder deaktiviert den Kündigungsschutz des Stacks. Funktioniert nur mit Botocore >= 1.7.18 |
region |
string | optional | AWS-Region, in der der Stack bereitgestellt werden soll (überschreibt den Standard) |
template_parameters |
dict | optional | Erforderliche Cloudformation-Stack-Parameter |
tags |
dict | optional | Tags, die mit dem Cloudformation-Stack verbunden sind |
Beispiele
Definiere Standardwerte, die auf alle Stacks angewendet werden (sofern nicht pro Stack-Definition überschrieben).
# Erzwinge, dass 'profile' für jedes Cloudformation-Stack-Element gesetzt werden muss
cloudformation_required:
- profile
cloudformation_defaults:
region: eu-central-1
Definiere Cloudformation-Stacks, die gerendert und bereitgestellt werden sollen.
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
Render nur deine Jinja2-Vorlagen, aber stelle sie nicht auf AWS bereit. Gerenderte Cloudformation-Dateien befinden sich im build/
-Verzeichnis dieser Rolle.
$ ansible-playbook play.yml -e cloudformation_generate_only=True
Verwendung
Einfach
Basisverwendungsbeispiel:
playbook.yml
- hosts: localhost
connection: local
roles:
- cloudformation
group_vars/all.yml
# Definiere Cloudformation-Stacks
cloudformation_stacks:
# Erster Stack
- stack_name: stack-s3
profile: testing
region: eu-central-1
template: files/cloudformation/s3.yml.j2
template_parameters:
bucketName: my-bucket
tags:
env: testing
# Zweiter Stack
- 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
Fortgeschritten
Fortgeschrittenes Verwendungsbeispiel, das die Rolle unabhängig in verschiedenen virtuellen Hosts aufruft.
inventory
[my-group]
infrastructure ansible_connection=local
application ansible_connection=local
playbook.yml
# Infrastruktur-Teil
- hosts: infrastructure
roles:
- cloudformation
tags:
- infrastructure
# Anwendungs-Teil
- 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 }}"
Vorlagen
Dieser Abschnitt gibt einen kurzen Überblick darüber, was mit Cloudformation-Vorlagen unter Verwendung von Jinja2-Direktiven gemacht werden kann.
Beispiel: Subnetzerteilungen
Die folgende Vorlage kann in unterschiedlichen Staging-Umgebungen ausgerollt werden und ist in der Lage, eine unterschiedliche Anzahl von Subnetzen einzuschließen.
Ansible-Variablen
---
# Datei: 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
---
# Datei: 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-Vorlage
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC-Vorlage
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 %}
Beispiel: Sicherheitsgruppen
Das Definieren von Sicherheitsgruppen mit IP-spezifischen Regeln ist sehr schwierig, wenn du die Umgebungsunabhängigkeit beibehalten und dennoch dieselbe Cloudformation-Vorlage für alle Umgebungen verwenden möchtest. Dies kann jedoch leicht überwunden werden, indem umgebungsspezifische Arraydefinitionen über Jinja2 bereitgestellt werden.
Ansible-Variablen
---
# Datei: staging.yml
# Staging ist offen, damit Entwickler sich von angeschlossenen VPNs verbinden können.
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
---
# Datei: production.yml
# Die Produktionsumgebung hat deutlich weniger Regeln und andere IP-Bereiche.
security_groups:
- protocol: tcp
from_port: 3306
to_port: 3306
cidr_ip: 10.0.15.1/32
Cloudformation-Vorlage
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC-Vorlage
Resources:
rdsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: RDS-Sicherheitsgruppe
{% 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 %}
Diff
Wenn du cloudformation_run_diff
aktivierst, kannst du eine zeilenweise Diff-Ausgabe deiner lokalen (jinja2-gerenderten) Vorlage im Vergleich zu der derzeit auf AWS bereitgestellten Vorlage sehen. Um dir einen Eindruck davon zu vermitteln, wie dies aussieht, siehe das folgende Beispielausgabe:
Stelle sicher, dass du Ansible mit --diff
ausführst, damit es funktioniert:
$ ansible-playbook play.yml --diff
Json-Diff
Um die Ausgabe im JSON-Diff-Modus zu erhalten, stelle cloudformation_diff_output
auf json
ein.
TASK [cloudformation : diff cloudformation template file] *********************************************
--- before
+++ after
@@ -38,7 +38,6 @@
"Type": "AWS::S3::BucketPolicy"
},
"s3Bucket": {
- "DeletionPolicy": "Retain",
"Properties": {
"BucketName": {
"Ref": "bucketName"
Yaml-Diff
Um die Ausgabe im YAML-Diff-Modus zu erhalten, stelle cloudformation_diff_output
auf yaml
ein.
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'
Abhängigkeiten
Diese Rolle hat keine Abhängigkeiten von anderen Rollen.
Anforderungen
Verwende mindestens Ansible 2.5, um den --check
Modus für Cloudformation ebenfalls nutzen zu können.
Das Python-Modul cfn_flip
ist erforderlich, wenn du das zeilenweise Diff zwischen lokalen und entfernten Cloudformation-Vorlagen verwendest (cloudformation_run_diff=True
). Dies kann einfach lokal installiert werden:
$ pip install cfn_flip
Lizenz
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