Pubblicato il 01/10/2023 da alnao nella categoria AWS

All’avvio di istanze EC2 è possibile indicare quale istruzioni eseguire, questo gruppo di istruzioni è chiamato User data, nonostante il nome possa essere forviante si tratta di un sistema di configurazione e controllo dell’avvio delle istanze, per ovvi motivi questo tipo di script può essere eseguito solo in istanze con sistema operativo GNU Linux ma non può essere usata con istanze con sistema operative MsWindows. Di default queste istruzioni vengono eseguite solo al primo avvio ma è possibile modificare la configurazione da console per eseguire lo script anche ogni riavvio come suggerito da una pagina del sito ufficiale. Ci sono due tipi di script: shell o cloud-init, il primo tipo viene usato per installare software o eseguire istruzione mentre il secondo viene usato quando c’è la necessità di gestire il collegamento tra l’istanza EC2 e altri servizi come CloudFormation, RDS. Un semplice esempio di uso di user-data è l’installazione di un web server LAMP con una piccola infrastuttura di webserver, database MySql e il motore Php. Nel caso di sistema operativo Linux con distruzione Amazon con il gestore di pacchetti yum:

#!/bin/bash
yum update -y
amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2
yum install -y httpd mariadb-server
systemctl start httpd
systemctl enable httpd
usermod -a -G apache ec2-user
chown -R ec2-user:apache /var/www
chmod 2775 /var/www
find /var/www -type d -exec chmod 2775 {} \;
find /var/www -type f -exec chmod 0664 {} \;
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php

Per verificare il corretto funzionamento degli script è possibile verificare i log scritti dal sistema nei due file dedicati:

/var/log/cloud-init.log
/var/log/cloud-init-output.log

e alcuni sotto-servizi del Cloud scrivono i corrispettivi files di log nella cartella specifica:

/var/log/amazon/

Il tipo cloud-init configura aspetti specifici di una nuova istanza Amazon Linux al momento del lancio come la configurazione del file delle chiavi private .ssh/authorized_keys. Le direttive utente cloud-init possono essere passate a un’istanza all’avvio nello stesso modo in cui viene passato uno script ma con una sintassi molto diversa: la prima riga deve iniziare con

#cloud-config

così da indicare il tipo di script, un esempio completo corrispondente allo script per l’installazione di un server LAMP:

#cloud-config
repo_update: true
repo_upgrade: all
packages:
- httpd
- mariadb-server
runcmd:
- [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ]
- systemctl start httpd
- sudo systemctl enable httpd
- [ sh, -c, "usermod -a -G apache ec2-user" ]
- [ sh, -c, "chown -R ec2-user:apache /var/www" ]
- chmod 2775 /var/www
- [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ]
- [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ]
- [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]

in questo esempio è possibile notare la distinzione tra la sezione “packages” dove sono elencati tutti i pacchetti da installare e la sezione “runcmd” con l’elenco dei comandi da eseguire.


Attraverso la CLI è possibile avviare istanze con uno specifico script salvato in un file

aws ec2 run-instances --image-id ami-XXXXX --count 1 
--instance-type m3.medium --key-name my-key-pair 
--subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 
--user-data file://my_script.txt

E’ possibile anche recuperare l’informazione dell’user data con il comando

aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData

Con la libreria boto3 di SDK è possibile indicare lo script user-data come parametro del metodo per avviare una istanza:

import boto3
ec2 = boto3.resource('ec2')
user_data_script = """#!/bin/bash
   echo "Hello, World!" > /home/ec2-user/hello.txt
"""
instance = ec2.create_instances(
  ImageId='<your-ami-id>',
  MinCount=1,
  MaxCount=1,
  InstanceType='t2.micro',
  UserData=user_data_script
)

Nei template CloudFormation è possibile indicare l’user data di una istanza EC2 come è possibile notare nell’esempio:

Resources:
  WebServerInstance:
    Type: 'AWS::EC2::Instance'
    Metadata:
      Comment1: >-
        Configure to install the Apache Web Server and PHP
      Comment2: Save website content to /var/www/html/index.php
      'AWS::CloudFormation::Init':
        configSets:
          InstallAndRun:
          - Install
          - Configure
        Install:
          packages:
            yum:
              mysql: []
              mysql-server: []
              mysql-libs: []
              httpd: []
              php: []
              php-mysql: []
          files:
            /var/www/html/index.php:
              content:
                ...: null
              mode: '000600'
              owner: apache
              group: apache
            /tmp/setup.mysql:
              content: !Join 
              - ''
              - - 'CREATE DATABASE '
                - !Ref DBName
                - |
                  ;
                - 'GRANT ALL ON '
                - !Ref DBName
                - .* TO '
                - !Ref DBUsername
                - '''@localhost IDENTIFIED BY '''
                - !Ref DBPassword
                - |
                ';
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/cfn-hup.conf:
              content: !Join 
              - ''
              - - |
                  [main]
                - stack=
                - !Ref 'AWS::StackId'
                - |+
                  #riga vuota
                - region=
                - !Ref 'AWS::Region'
                - |+
                  #riga vuota
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join 
              - ''
              - - |
                  [cfn-auto-reloader-hook]
                - |
                  triggers=post.update
                - >
 path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                - 'action=/opt/aws/bin/cfn-init -v '
                - ' --stack '
                - !Ref 'AWS::StackName'
                - ' --resource WebServerInstance '
                - ' --configsets InstallAndRun '
                - ' --region '
                - !Ref 'AWS::Region'
                - |+
                  #riga vuota
                - |
                  runas=root
          services:
            sysvinit:
              mysqld:
                enabled: 'true'
                ensureRunning: 'true'
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                - /etc/cfn/cfn-hup.conf
                - /etc/cfn/hooks.d/cfn-auto-reloader.conf
        Configure:
          commands:
            01_set_mysql_root_password:
              command: !Join 
              - ''
              - - mysqladmin -u root password '
                - !Ref DBRootPassword
                - ''''
                  test: !Join 
                - ''
                - - '$(mysql '
                - !Ref DBUsername
                - ' -u root --password='''
                - !Ref DBRootPassword
                - ''' >/dev/null 2>&1 </dev/null); (( $? != 0 ))'
            02_create_database:
              command: !Join 
                - ''
                - - mysql -u root --password='
                  - !Ref DBRootPassword
                  - ''' < /tmp/setup.mysql'
              test: !Join 
                - ''
                - - '$(mysql '
                  - !Ref DBUsername
                  - ' -u root --password='''
                  - !Ref DBRootPassword
                  - ''' >/dev/null 2>&1 </dev/null); (( $? != 0 ))'
  Properties:
    ImageId: !FindInMap 
      - AWSRegionArch2AMI
      - !Ref 'AWS::Region'
      - !FindInMap 
        - AWSInstanceType2Arch
        - !Ref InstanceType
        - Arch
    InstanceType: !Ref InstanceType
    SecurityGroups:
    - !Ref WebServerSecurityGroup
    KeyName: !Ref KeyName
    UserData: !Base64 
      'Fn::Join':
      - ''
      - - |
          #!/bin/bash -xe
        - |
          yum install -y aws-cfn-bootstrap
        - |
          # Install the files and packages from the metadata
        - '/opt/aws/bin/cfn-init '
        - ' --stack '
        - !Ref 'AWS::StackName'
        - ' --resource WebServerInstance '
        - ' --configsets InstallAndRun '
        - ' --region '
        - !Ref 'AWS::Region'
        - |+

In questo semplice esempio è possibile notare che sono presenti script di entrambi i tipi di user data separati:

  • cloud-init: in una sezione Metadata vengono divisi i comandi tra package, files, service e command, si rimanda alla documentazione ufficiale per la descrizione approfondita della sintassi di queste sezioni.
  • script: nello script viene installato il pacchetto “aws-cfn-bootstrap” e successivamente viene eseguito un comando “cfn-init” per avviare lo script indicato nel cloud-init.

Questa tecnica è necessaria per permettere a CloudFormation di avviare i vari script nella istanza in fase di creazione, si rimanda alla documentazione ufficiale. Molti esempi di user data sono consultabili nel sito ufficiale.

Pubblicato il 01/10/2023 da alnao nella categoria Debian

Per utilizzare al meglio i sistemi Debian è indispensabile conoscere la gestione dei pacchetti: i pacchetti sono un insieme di file organizzati e compressi in directory in modo che possano essere installati nel sistema velocemente e con ordine; esistono molti modi di compilare e preparare i pacchetti, Debian ha imposto da anni un suo standard che è facilmente riconoscibile per i file di estensione deb mentre altre distribuzioni possono avere altri formati come rpm. Uno dei motivi per cui Debian è molto famosa è proprio per la gestione eccellente dei pacchetti perché, pur contando oltre 25.000 pacchetti ufficiale, esiste un repository centrale che gestisce le le dipendenze in modo semplice e ordinato: ogni pacchetto ha una versione e una lista di dipendenze infatti ogni pacchetto al suo interno ha l’informazione di quali pacchetti sono necessari e con i quali entra in conflitto, per esempio il pacchetto “apache2-mpm-prefork” dipende dal pacchetto “apache2.2-common” e entra in conflitto con il pacchetto “apache2-mpm”, tutto questo dipende anche delle versioni, cioè ogni pacchetto è segnato anche da una versione, per esempio nel mio sistema è installato il pacchetto “apache2-mpm-prefork” alla versione 2.2.8-3: questo vuol dire che il pacchetto è alla versione 2.2.8 ma è la terza compilazione del pacchetto. Per fortuna i programmi che andremo ad usare gestiscono automaticamente con ordine e precisione i pacchetti quindi l’utente non deve mai preoccuparsi di risolvere le dipendenze.

La gestione dei pacchetti Debian nel sistema si basa sul componente DPKG (abbreviazione di Debian PacKaGe): è usato per installare, disinstallare ed ottenere informazioni sul singolo pacchetto in formato standard deb. Questo è un tool di basso livello e viene sempre affiancato da APT (che è l’abbreviazione di Advanced Packaging Tool) che è il gestore standard di pacchetti software della distribuzione Debian. Con APT è possibile anche configurare diversi mirror-sorgenti di pacchetti (sorgenti remote in internet, cdrom, DVD). Tutti i comandi dei due programmi sono:

  • # apt-get update aggiorna la lista dei pacchetti dopo la configurazione di un nuovo mirror
  • # apt-get install nomepacchetto installa il pacchetto nel sistema, scegliendo l’ultima versione disponibile ed risolvendo le dipendenze in maniera automatica (installando i pacchetti necessari e togliendo i pacchetti in conflitto)
  • # apt-get remove nomepacchetto rimuove il pacchetto e tutti i pacchetti che dipendono da esso
  • # apt-get --purge remove nomepacchetto rimuove il pacchetto e tutti i pacchetti che dipendono da esso compresi anche tutti i file di configurazione eventualmente presenti
  • # apt-get upgrade aggiorna tutti i pacchetti se sono disponibili aggiornamenti e verifica tutte le dipendenze delle nuove versioni
  • # apt-get clean cancella tuti i file temporanei di apt-get cioè i file deb scaricati e già installati
  • # apt-get install -f verifica che tutti i pacchetti siano installati e configurati correttamente, in caso di errori sistema automaticamente le dipendenze
  • # dkpg --configure -a come il precedente ma funziona anche in casi estremi e risolve tutti i problemi di conflitto tra i pacchetti
  • # dpkg-reconfigure pacchetto riconfigura un pacchetto già installato
  • # dpkg -i pacchetto installa un pacchetto da un file deb
  • # dpkg -r pacchetto rimuove un pacchetto
  • # dpkg --get-selections > nomefile.txt salva su un file l’elenco dei pacchetti installati
  • # dpkg --set-selections < nomefile.txt imposta i pacchetti presenti sul file
  • # apt-get moo regala all’utente un simpatico messaggio

All’interno di un sistema, la lista dei mirror-sorgenti è elencata nel file

/etc/apt/sources.list

e da tutti i file contenuti nella sotto-cartella

/etc/apt/sources.list.d/

e si possono creare dei file con estensione list per aggiungere altri mirror specifici. In questi file list, ad ogni riga corrisponde una sorgente che può essere un DVD, un mirror in internet oppure una cartella del sistema locale. Ogni riga di questi file è del tipo:

deb http://host/debian distribuzione sezione1 sezione2 sezione3
deb-src http://host/debian distribuzione sezione1 sezione2 sezione3

La prima parola di ogni riga (deb o deb-src) indica il tipo di archivio: se contiene pacchetti binari (deb) oppure indica se l’archivio contiene i pacchetti sorgente (deb-src), la seconda parte della riga indica l’indirizzo della sorgente. Nel terzo parametro si indica la distribuzione: di solito si tratta di uno dei tre rami di sviluppo: stable, testing o unstable, nelle righe di configurazione è possibile indicare esplicitamente il nome della versione come etch, sid o sarge). L’elenco delle sezioni indicano quali parti della distribuzione dovranno essere gestite, tipicamente si trova main (i pacchetti completamente liberi) oppure non-free (cioè i pacchetti rilasciati sotto una licenza non libera) oppure contrib (pacchetti liberi che però dipendono da altri non liberi). Al termine di ogni modifica di questi file di configurazione è necessario lanciare il comando per aggiornare il database di sistema:

# apt-get update

In precedenti versioni di questo documento si indicava all’utente di modificare manualmente i file di list per inserire i mirror manualmente, questa operazione manuale è stata sostituita da operazioni più semplici che verranno introdotte man mano che sarà necessario installare pacchetti specifici non compresi nei mirror ufficiali di Debian che sono inseriti in automatico all’installazione del sistema base. I programmi di gestione APT & DPKG non sono stati studiati per essere interfacciati graficamente, quindi sono stati sviluppati e sono presenti in Debian diversi strumenti grafici che permettono di gestire i pacchetti attraverso una interfaccia grafica che può risultare più intuitiva all’utente meno esperto: il più importante programma per desktop per la gestione dei pacchetti è Synaptic che spesso si può trovare nei menù anche con il nome in italiano di Gestore pacchetti, la potenza di questo programma è la semplicità d’uso rispetto ad altri programmi simili come Aptitude e Adept che sono molto più poveri di funzioni. Con Synaptic è possibile vedere la lista dei pacchetti divisi in sezione per argomento (sistema, grafici, editor,…), per stato (installati, aggiornabili, non installati, corrotti), per origine (DVD, debian.org, ecc…) e la possibilità di cercare i pacchetti con una semplice ricerca testuale sui nomi e sulle descrizioni dei pacchetti stessi. La comodità principale di questo programma è la possibilità di gestire gli aggiornamenti del sistema con un semplice click su pulsante.

Nei sistemi Debian è possibile trovare diversi programmi che permettano la configurazione del sistema, nei vari menu dei desktop si possono trovare diverse voci all’interno della categoria Strumenti di sistema però per GNU Linux è stato sviluppato un potentissimo programma per il controllo generale: WebMin che prevedete moltissimi moduli al proprio interno e permette all’utente di amministrare tutti i componenti del sistema, uno dei grandi vantaggi di questo pacchetto è che si tratta di una applicazione web quindi viene usata tramite browser anche da remoto. Rispetto alle precedenti versioni, WebMin non è più all’interno dei mirror ufficiali di Debian e quindi bisogna configurare una sorgente esterna con i comandi:

# apt-get install perl libnet-ssleay-perl openssl libauthen-pam-perl 
# apt-get install libpam-runtime libio-pty-perl apt-show-versions python3 apt-transport-https
# apt-get install wget net-tools ufw
# cd /etc/apt/sources.list.d/
# echo "deb http://download.webmin.com/download/repository sarge contrib" > webmin.list
# wget http://www.webmin.com/jcameron-key.asc
# apt-key add jcameron-key.asc 
# apt-get update 
# apt-get install webmin
# ufw allow 10000/tcp
# ufw reload
# ufw enable

con questi comandi abbiamo installato dei pacchetti propedeutici, abbiamo scaricato il file list del mirror e la sua chiave di sicurezza, poi con il comando di apt abbiamo aggiornato l’elenco dei pacchetti e installato il programma, al termine è necessario configurare la rete visto che il programma utilizza la porta 10.000 dei sistemi. Dopo l’installazione è possibile già vedere se è disponibile e funzionante con il comando:

# systemctl status webmin

Se non dovesse essere avviato è possibile lanciare lo start con il comando:

# systemctl start webmin

Per poter accedere al pannello di controllo basta lanciare da un browser l’url:

https://localhost:10000/

nei comandi indicati c’è anche la configurazione del firewall ufw che bloccherebbe l’applicazione trattandosi di una applicazione di rete sulla porta 10000. All’interno delle ultime versioni di Webmin, c’è la possibilità di collegarsi alla console shell con l’icona >_ presente nel menù di sinistra al tab “dashbord” questo permette di usare il terminale da remoto anche se io lo ritengo molto scomodo.

WebMin è uno strumento molto potente ma anche troppo! Bisogna sempre prestare attenzione alle configurazioni eseguite e controllare più volte le operazioni potenzialmente distruttive per il sistema.

Il programma di installazione prevede la creazione di un primo utente che viene utilizzato al primo accesso, attraverso i vari tool di controllo disponibili è possibile creare e configurare altri utenti se necessario con la possibilità di raggrupparli e gestire l’accesso. Il gestore della login che compare all’avvio del sistema grafico si chiamata GDM (abbreviazione di Gnome Desktop Manager) esistono anche altri gestori ma è sconsigliato l’utilizzo per utente non esperti. Per gestire gli utenti è possibile usare i vari programmi disponibili nei browser ma è consigliato usare WebMin appena installato visto che ha una interfaccia molto semplice e intuitiva per la gestione degli utenti e dei gruppi. Il pannello di controllo WebMin è molto utile anche per la gestione di tutte le parti di un sistema: lo schedulatore di sistema CronTab, le configurazioni di rete, la gestione dei backup, il monitoraggio dei log e i demoni/server come sarà descritto in una sezione dedicata. In realtà è iii sconsigliato iii per alcuni temi specifici come la gestione delle partizioni (molto meglio Gparted) e tutto quello che ha a che fare con l’hardware e i dispositivi esterni.

Pubblicato il 01/10/2023 da alnao nella categoria Css3 & Bootstrap

In fase di studio del layout di un sito bisogna considerare la struttura a blocchi dei siti responsive: non è possibile incastrare le componenti senza allineamenti e bisogna evitare di creare strutture a tetris, come in immagine, che potrebbero anche essere impossibili con tecnologie HTML e non possono essere facilmente adattate ai dispositivi mobili.

Durante lo studio e la progettazione di un sito responsive bisogna sempre tenere conto che gli oggetti dovranno essere visualizzati anche sui dispositivi mobili come gli smartphone e monitor molto grandi come i monitor molto grandi quindi nel disegno di un sito non si può pensare di inserire un oggetto statico ma bisogna sempre pensare alla sua rappresentazione grafica sui diversi dispositivi, allo stesso tempo bisogna cercare di non limitare troppo il “canale” internet, trovare un compromesso è possibile usando il box-modeling e lo studio degli elementi flessibili, in questo articolo sarà introdotto il primo concetto mentre il secondo sarà introdotto in futuri articoli.

Il concetto di box-modeling è uno dei punti più importanti dei fogli di stile CSS e ogni designer deve conoscere molto bene questi concetti: fin dalla prima versione dell’HTML e dei CSS ogni programmatore è sempre afflitto da dove e come posizionare gli oggetti HTML rispetto agli altri, infatti per box-modeling si intende definire le posizioni e le distanze tra i vari oggetti usando le varie proprietà di altezza, larghezza, margine, bordi e padding. Fin dalla versione 2 di CSS si è definito uno standard sui box e non è variata molto nella versione 3: ogni elemento ha un margine, un bordo, un padding, larghezza e altezza che vengono definite con proprietà specifiche:

le proprietà di larghezza e altezza (width e height) imposta le dimensioni di elementi esclusi padding, margini e bordi che vengono sommati a posteriori. Per rendere CSS più flessibile possibile è possibile modificare l’impostazione della somma delle larghezze usando la proprietà box-sizing che può assumere due valori:

  • content-box: il valore di larghezza fa riferimento all’area del contenuto (valore di default)
  • border-box: il valore di larghezza fa riferimento al box nella sua interezza, comprendendo anche il padding e il bordo.

Quindi, per esempio:

#divProva{
  width:100px; 
  padding:2px; 
  border: 1px solid black; 
  box-sizing:border-box;
}

la larghezza di 100 pixel comprende anche bordi e padding.


Con le proprietà flexbox sono state introdotte alcune nuove caratteristiche per creare strutture di layout non lineari come previsto normalmente da CSS, senza i quali al programmatore rimane solo l’uso della proprietà float. Questa è stata pensata per rendere semplice la gestione di elementi interni ad un elemento contenitore e per risolvere le varie problematiche legate alla centratura degli elementi rispetto agli assi verticali e orizzontali. Per usare queste proprietà bisogna tenere conto di due aspetti costruttivi:

  • un elemento contenitore che contiene gli elementi flessibili indicato con la proprietà display:flex
  • gli elementi flessibili dove non bisogna dedicare nessuna proprietà specifica.

All’elemento padre si deve poi aggiungere la proprietà addizionale per indicare l’allineamento verticale con la proprietà align-items: center;

Agli elementi figli è possibile poi aggiungere delle caratteristiche ulteriore:

  • modificare l’allineamento dello specifico elemento con la proprietà align-self
  • modificare l’ordine di visualizzazione con la proprietà order

Un esempio pratico è:

<div style="display:flex;align-items: center;">
  <div style="background-color:red;">rosso</div>
  <div style="background-color:yellow;">giallo<br /> a capo</div>
  <div style="background-color:green;text-align:center;">verde</div>
  <div style="background-color:gold;order:-2;">oro con ordine</div>
  <div style="background-color:lightblue;align-self:flex-start;">azzurro</div>
<div>

E il risultato di questo semplice esempio è:

rosso
giallo
a capo
verde
oro con ordine
azzurro
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