Pubblicato il 23/09/2023 da alnao nella categoria AWS
CloudFormation è uno dei servizi più complessi di AWS ma allo stesso tempo è uno dei più potenti. In questa serie di articoli vengono esposti alcuni esempi completi di template avanzati con lo scopo di costruire infrastrutture usabili in ambienti reali. Si rimanda sempre alla documentazione ufficiale per tutti i dettagli del servizio, bisogna sempre ricordare che l’uso di questo è gratuito ma tutte le risorse create e gestite sono a pagamento quindi bisogna sempre prestare attenzione alla creazione di stack con risorse a pagamento. In fase di realizzazione di template è possibile usare la tecnica Nested stacks per creare template riusabili e innestabili in altri template evitando di scrivere file yaml (o json) lunghi e molto complessi, nella documentazione ufficiale è specificata la semplice definizione:
Nested stacks are stacks created as part of other stacks
Con questa tecnica infatti è possibile usare il tipo  AWS::CloudFormation::Stack per eseguire l’innesto di un template dentro ad un altro, l’uso di questa tecnica è frequente quando si vogliono usare template già pronti dal sito ufficiale o personalizzati integrandoli in un template principale (spesso chiamato root nei vari documenti o siti di riferimento).

Un semplice esempio è creare un template con innestato un template ufficiale:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Template che crea una VPC richiamando il template ufficiale
Parameters:
  CidrBlockVPC:
    Description: 'The IP address range to VPC'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.184.0.0/16
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x
  CidrBlockSubnetA:
    Description: 'The IP address range to Subnet A'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.184.1.0/24
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  CidrBlockSubnetB:
    Description: 'The IP address range to Subnet B'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.184.2.0/24
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  CidrBlockSubnetC:
    Description: 'The IP address range to Subnet A'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.184.3.0/24
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  CidrBlockSubnetD:
    Description: 'The IP address range to Subnet B'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.184.4.0/24
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Resources:
  VPC:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/infrastructure/vpc.yaml
      Parameters:
        EnvironmentName: !Ref AWS::StackName #usato come tag per le risorse
        VpcCIDR: !Ref CidrBlockVPC # "10.84.0.0/16"
        PublicSubnet1CIDR: !Ref CidrBlockSubnetA # "10.84.1.0/24"
        PublicSubnet2CIDR: !Ref CidrBlockSubnetB # "10.84.2.0/24"
        PrivateSubnet1CIDR: !Ref CidrBlockSubnetC # "10.84.3.0/24"
        PrivateSubnet2CIDR: !Ref CidrBlockSubnetD # "10.84.4.0/24"
Un occhio attento avrò notato che in questo esempio viene usato un template da un bucket S3, infatti esiste un repository ufficiale dove ci sono moltissimi template già pronti all’uso:
https://github.com/aws-samples
Nella maggior parte degli articoli che seguiranno verranno usati alcuni template pubblici ufficiali ma sarà sempre chiara la sorgente a la tecnica di import, in alcuni esempi i template ufficiali vengono scaricati e modificati ma è sempre indicata la sorgente originale e viene sempre indicata la risorsa aggiunta al template.

All’interno dei template CloudFormation è possibile creare delle condizioni in una sezione Conditions, queste possono gestire la creazione di alcune risorse in maniera semplice in modo da poter avere un unico template con delle condizioni al suo interno. Il caso d’uso più semplice è creare lo stesso templare per l’ambiente di produzione e l’ambiente di test ma creare risorse diverse a seconda del tipo di ambiente, spesso questa tecnica viene chiamata come enviroments-template. La base è dichiarare la condizione e il suo valore che può dipendere da parametri o mappings, per esempio:
Conditions:
  CreateVolume: !Equals [!Ref EnvName, prod]
All’interno di queste condizioni possono essere usate tutte queste istruzioni logiche:
  • Fn::And
  • Fn::Equals
  • Fn::If
  • Fn::Not
  • Fn::Or
All’interno delle risorse è possibile aggiungere una proprietà specifica con il riferimento al nome della condizione dichiarata nella sezione Condition: CreateVolume. Esaminando un esempio di creazione di un disco solo se si tratta di un ambiente di produzione mentre il disco non verrà creato se si tratta di altro ambiente:
Esempio20conditionsVolumeAttachment:
  Type: AWS::EC2::VolumeAttachment
  Condition: CreateVolume
  Properties:
    InstanceId: !Ref Esempio20conditionsInstance
    VolumeId: !Ref Esempio20conditionsVolume
    Device: /dev/sdh
Esempio20conditionsVolume:
  Type: AWS::EC2::Volume
  Condition: CreateVolume
  Properties:
    Size: 10
    AvailabilityZone: !Ref RegionAZ
All’interno della sezione di Outputs è possibile fare rifermenti a valori condizionali, per esempio è possibile ritornare il riferimento al Volume creato, anche in questo caso il messaggio di output sarà creato a seconda della condizione dichiarata all’inizio del template.
Outputs:
  StackName:
    Description: Deployed StackName for update
    Value: !Ref AWS::StackName
  VolumeId:
    Description: Volume ID if created
    Condition: CreateVolume
    Value: !Ref Esempio20conditionsVolume

All’interno dei template CloudFormation è possibile usare una sezione Mappings per la definizione di coppie chiave-valori come struttura dati utilizzabili nei template con l’operatore Fn::FindInMap, la documentazione ufficiale del comando è ricca di esempi. Un esempio di utilizzo di questa tenica può essere la definizione di un blocco per descrivere la dimensione di istanze a seconda di diversi ambienti:
Mappings:
  EnvInstance:
    dev:
      EC2Type: t2.micro
    prod:
      EC2Type: t2.small
Il valore è recuperabile all’interno dei template nel blocco di una risorsa usando FindInMap con i tre parametri: il nome definito nel mapping, il valore recuperato da parametri o costanti e il nome della proprietà:
InstanceType: !FindInMap [EnvInstance, !Ref 'EnvName', EC2Type]
Un esempio dove sono utilizzate le tecniche di condition e mapping combinate per la creazione di istanze EC2 è disponibile al solito repository:
https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio05conditions
MENU