cytopia.cloudformation

Ansible-Rolle: Erstelle Cloudformation-Stacks

Motivation | Installation | Funktionen | Variablen | Verwendung | Vorlagen | Diff | Abhängigkeiten | Anforderungen | Lizenz

Build-Status Ansible Galaxy Release

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.

  1. 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.
  2. 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.
  3. 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

MIT-Lizenz

Copyright (c) 2017 cytopia

Über das Projekt

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

Installieren
ansible-galaxy install cytopia.cloudformation
Lizenz
mit
Downloads
5.2k
Besitzer
DevOps Engineer