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.

Nested stacks are stacks created as part of other stacks
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).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"
https://github.com/aws-samples
Conditions: CreateVolume: !Equals [!Ref EnvName, prod]
- Fn::And
- Fn::Equals
- Fn::If
- Fn::Not
- Fn::Or
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
Outputs: StackName: Description: Deployed StackName for update Value: !Ref AWS::StackName VolumeId: Description: Volume ID if created Condition: CreateVolume Value: !Ref Esempio20conditionsVolume
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
InstanceType: !FindInMap [EnvInstance, !Ref 'EnvName', EC2Type]
https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio05conditions
CloudWatch è il principale servizio di monitoraggio degli eventi nel Cloud AWS, con il quale è possibile raccogliere log e dati in tempo reale, questi dati vengono poi resi disponibili in un pannello di controllo nella console web, studiata per la manutenzione dell’infrastruttura e delle applicazioni in esecuzione. Con CloudWatch e le sue funzionalità di Log e Alarm, è possibile ridurre al minimo la logica da programmare per il monitoraggio, avendo a disposizione un sistema molto robusto e flessibilo.
Il servizio si basa su alcuni concetti:
- Log: un registro di log proveniente da tutti gli altri servizi, i dati vengono strutturati in Namespace e metriche
- Namespace: è un contenitore di metriche che risultano separate
- Metric: rappresenta un insieme di dati in ordine temporale pubblicati su CloudWatch, vengno rappresetnate come variabili da monitorare (a volte il termine Metric viene tradotto in italiano con la parola Parametro)
- Dashboard: nella console web è disponibile un pannello di controllo personalizzabile con il quale è possibile monitorare il sistema e gli eventi passati
- Alarm: dalle metriche si possono creare allarmi che generano eventi in base ad eventi. L’allarme più semplice consiste nell’avvisare persone inviando un messaggio quando si verifica un evento registrato in una metrica
- CloudWatch agent: sistema per inserire log in Metric provenienti da sistemi esterni al sistema Cloud AWS.
Nella documentazione ufficiale è presente uno schema che descrive il funzionamento del servizio:
Bisogna prestare attenzione quando viene usato questo servizio in quanto non è gratuito visto che alcune funzionalità sono a pagamento in base all’uso e in base a quante query vengono eseguite nel motore che gestire log ed eventi, è consigliato leggere attentamente la pagina ufficiale.
La CLI mette a disposizione una serie di comandi per la gestione di comandi, la documentazione ricca di esempi è sempre il punto di riferimento princpale. I comandi principali per gestire le metriche si basano sul comando:
aws cloudwatch list-metrics --namespace "AWS/Lambda"
Per gestire gli allarmi è possibile recupeare la lista con i comandi:
aws cloudwatch describe-alarms aws cloudwatch describe-alarms --alarm-names "nome-alarm"
Per scatenare un allarme è possibile usare lo stesso comando con dei parametri specifici:
aws cloudwatch set-alarm-state --alarm-name "nome-alarm" --state-value ALARM --state-reason "testing purposes"
La gestione dei log da riga di comando può essere scomoda a causa della numerosità dei log comunque esiste il comando per recuperare l’elenco dei gruppi di log e i singoli eventi salvati nel registro con la possibilità di filtrare per pattern e orario del log:
aws logs describe-log-groups aws logs filter-log-events --log-group-name /aws/lambda/nome-lambda aws logs filter-log-events --log-group-name /aws/lambda/nome-lambda --filter-pattern ERROR aws logs filter-log-events --log-group-name /aws/lambda/nome-lambda --start-time 1688977737000 --end-time 1688977738000 aws logs filter-log-events --log-group-name /aws/lambda/nome-lambda --output text
Con CloudFormation è possibile gestire i log e i permessi che le varie risorse devono avere per scrivere nei log, per esempio in un template per la creazione di una lambda, oltre alla creazione della Lambda, nella sua execution role creata come regola IAM è necessario aggiungere la policy per permettere alla funzione di creare il gruppo di log e a scriverci, un semplice e classico esempio è:
LambdaIamRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: root PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: 'arn:aws:logs:*:*:*'
Tramite CloudFormation è possibile anche creare Allarmi, Dashboard e Metriche, la documentazione ufficiale è completa e ricca di esempi, per la gestione dei log è disponibile una pagina dedicata.
La libreria SDK mette a disposizione una libreria per la gestione del servizio CloudWatch, dei log, degli alert e delle metriche tramite la solita libreria boto3. Le principali funzionalità sono recuperare la lista delle metriche disponibili ed eseguire una query su una metrica per recuperare la lista dei log degli eventi:
def get_metrics(profile_name): boto3.setup_default_session(profile_name=profile_name) cloudwatch = boto3.client('cloudwatch') lista=[] paginator = cloudwatch.get_paginator('list_metrics') for response in paginator.paginate( Dimensions=[{'Name': 'LogGroupName'}], MetricName='IncomingLogEvents', Namespace='AWS/Logs'): lista=lista+response['Metrics'] return lista def get_metric_group_log(profile_name,group_name): # Create CloudWatchLogs client boto3.setup_default_session(profile_name=profile_name) cloudwatch_logs = boto3.client('logs') # List subscription filters through the pagination interface paginator = cloudwatch_logs.get_paginator('describe_log_groups') lista=[] for response in paginator.paginate(logGroupNamePrefix=group_name): lista=lista+response['logGroups'] #for i in response['logGroups']: #print(i['logGroupName']) return lista def get_metric_log(profile_name,group_name,hours): boto3.setup_default_session(profile_name=profile_name) cloudwatch_logs = boto3.client('logs') query = "fields @timestamp, @message" start_query_response = cloudwatch_logs.start_query( logGroupName=group_name, startTime=int((datetime.today() - timedelta(hours=hours)).timestamp()), endTime=int(datetime.now().timestamp()), queryString=query, ) query_id = start_query_response['queryId'] response = None while response == None or response['status'] == 'Running': #print('Waiting for query to complete ...') time.sleep(1) response = cloudwatch_logs.get_query_results( queryId=query_id ) return response["results"]
L’icona del servizio è proprio il logo del sito
Per serverless si intende una architettura Cloud nella quale la gestione dei server viene delegata al gestore del Cloud e i programmatori possono concentrarsi al 100% sugli sviluppi. AWS ha investito molto sui servizi di questo tipo basando quasi interamente il Cloud su questo tipo di modello. Il più famoso servizio di questo tipo è Lambda che permette di creare applicazioni scrivendo piccole funzioni, altri servizi di tipo serverless saranno descritti in futuri articoli.
Nota: con il termine “serverless” non si intende l’assenza di server ma si intende che il server può essere configurato dall’utente ma non è possibile prenderne il controllo.
Il servizio Lambda prevede che venga indicata l’architettura (64 o 32 bit) e la quantità di memoria RAM, al resto di pensa AWS. Il servizio prevede un limite tecnologico sulle elaborazioni di ogni funzione: una esecuzione può durare al massimo 15 minuti, oltre questo limite il processo di interrompe per time-out. Per tutte le informazioni riguardo a questo servizio si rimanda alla documentazione ufficiale dove sono sono presenti anche esempi per tutti i linguaggi di programmazione supportati come Python, Java e Node.
Nella console web di AWS per ogni lambda si hanno a disposizione quattro tab per la gestione delle funzioni:
- il codice, la configurazione dell’architettura e la dichiarazione del nome tecnico della funzione (handler)
- il sistema di testing della funzione
- il monitor della funzione con grafici e link per consultare i log in CloudWatch
- le configurazioni avanzate tra cui: il trigger chiamante come EventBridge, la execution rule e la resource-based policy statements, le variabili d’ambiente (Environment variables), la configurazione della Vpc e le configurazioni dei sistemi di chiamata asincrona (come SQS)
La console web mette a disposizione una procedura guidata per la creazione di una Lambda Function, nella procedura l’utente deve inserire i dati minimi: il nome, il runtime (Node, Python, Java, …). E’ possibile modificare alcune configurazione di default come la “execution role” che viene creata di default con solo la “permission” per permettere alla funzione di scrivere i log nella CloudWatch Logs, si rimanda alla documentazione ufficiale per maggior informazioni riguardo alle “execution role” e alle altre configurazioni delle lambda function da Console. Di default la Lambda creata in Console nel linguaggio Python viene creata con il codice di default molto semplice:
nel caso di linguaggio Java invece è necessario creare un Jar come spiegato nell’esempio disponibile nel repository:
https://github.com/alnao/AwsLambdaExamples/tree/master/java-maven-example01-console
In questo esempio sono introdotti anche i comandi maven per creare il progetto Java di base, è possibile creare lo scheletro di un progetto AWS-Lambda usando un archetipo specifico:
mvn archetype:generate -DarchetypeGroupId=xxxxx -DarchetypeArtifactId=archetype-lambda -DarchetypeVersion=2.15.79
La CLI mette a disposizione una serie di comandi per creare e gestire le lambda function, la sintassi del comando base è
aws lambda create-function --function-name java-maven-example01-cli --zip-file fileb://target/java-maven-example01-console-1.0-SNAPSHOT.jar --runtime java8 --handler it.alnao.App::handleRequest --role arn:aws:iam::740456629644:role/lambda-role
Tramite questo comando è possibile invocare, aggiornare e recuperare le informazioni riguardo alla lambda, una serie di esempi per la gestione di una lambda:
aws lambda invoke --function-name java-maven-example01-cli outputfile.txt aws lambda update-function-code --function-name java-maven-example01-cli --zip-file fileb://target/java-maven-example01-console-1.0-SNAPSHOT.jar aws lambda list-functions --max-items 10 aws lambda get-function --function-name java-maven-example01-cli aws lambda delete-function --function-name java-maven-example01-cli aws lambda get-function-configuration --function-name my-function
La CLI mette a disposizione un comando specifico per la gestione dei servizi serverless e in particolare le lambda function, chiamato Serverless Application Model abbreviato con la sigla SAM. Nella documentazione ufficiale sono descritti tutti i comandi messi a disposizione, è disponibile una guida per l’installazione. Il comando più usato è il:
sam deploy --guided
usato per eseguire il rilascio di una Lambda, il comando crea un template CloudFormation, per esempio:
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: AppFunction: Type: AWS::Serverless::Function Properties: Runtime: java8 Handler: it.alnao.App::handleRequest Timeout: 60 MemorySize: 512 CodeUri: ./target/java-maven-example01-console.jar
e successivamente esegue il rilascio di uno stack salvando un file di configurazione samconfig.toml
dove sono indicati tutti i parametri per la creazione dello stack.
Con CloudFormation è possibile gestire Lambda, le “execution role” e tutti i servizi che possono iteragire con le funzioni. L’esempio più semplice è creare una Lambda che legge un file da un Bucket S3, esempio classico consultabile nel solito repository. In questo esempio si vede la dichiarazione della Lambda con il codice Python in-line nel templare, questa tecnica è ovviamente da evitare e conviene sempre fare un file py separato, in questo esempio viene usata questa tecnica solo a titolo esplicativo:
FunzioneLambda: Type: 'AWS::Lambda::Function' Properties: Code: ZipFile: | import json def lambda_handler(event,context): for record in event['Records']: print("Esecuzione" + json.dumps(event) ) bucket_name = record['s3']['bucket']['name'] key_name = record['s3']['object']['key'] print('Key found: ' + key_name + ' in Bucket: ' + bucket_name) return {'statusCode': 200 , 'body': 'OK'} Handler: index.lambda_handler Role: !GetAtt LambdaIAMRole.Arn Runtime: python3.9 Timeout: 5
Come ripetuto più volte, la tecnica di inserire il codice Python all’interno dei template CloudFormation è una cosa da evitare, usando un file separato con estensione “py” è richiamabile indicando il nome della sottoscartella nel “CodiceUri”, mentre nel Handler si indica il nome del file e il nome del metodo (che tipicamente è lambda_handler):
ExternalPyLambda: Type: AWS::Serverless::Function Properties: CodeUri: lambda Handler: esempio_file.lambda_handler Runtime: python3.8 MemorySize: 512 Timeout: 900
Oltre alla definizione della Lambda, all’interno del template CloudFormation, bisogna aggiungere due elementi:
- la regola IAM detta execution role per gestire i permessi di cosa può fare la lambda. risorsa di tipo
AWS::IAM::Role
- la regola di invocazione detta resource base policy per permettere al chiamante di invocare la funzione Lambda, risorsa di tipo
AWS::Lambda::Permission
in questo esempio la execution role permette al codice di accedere alla notifica da S3 e di scrivere i log, mentre la resource policy permette al bucket S3 di invocare la funzione lambda. Bisogna sempre ricordare che senza queste regole il sistema blocca le esecuzioni in quanto su AWS tutto è negato a meno che non sia specificato in una regola IAM, si rimanda alla documentazione ufficiale per maggior in formazioni.
LambdaInvokePermission: Type: 'AWS::Lambda::Permission' Properties: FunctionName: !GetAtt S3Notification.Arn Action: 'lambda:InvokeFunction' Principal: s3.amazonaws.com SourceArn: !Join - '' - - 'arn:aws:s3:::' - !Ref BucketName LambdaIAMRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: root PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 's3:GetBucketNotification' - 's3:PutBucketNotification' Resource: !Join - '' - - 'arn:aws:s3:::' - !Ref BucketName - Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: 'arn:aws:logs:*:*:*'
Nelle funzioni lambda è possibile usare le librerie messe a disposizione dal SDK per interagire con altri gli altri servizi, per esempio nelle lambda function è possibile usare la libreria Boto3 per collegarsi ad altri servizi AWS, per esempio è possibile accedere ad un bucket con poche righe di codice:
import boto3 s3 = boto3.client('s3') s3_resource = boto3.resource('s3') ... def lambda_handler(event,context): print("Esecuzione" + json.dumps(event) ) ... #esempio per leggere un oggetto da un bucket s3_object = s3.get_object(Bucket=bucket_name, Key=key_name) ... #esempio per copiare un file da un bucket ad un altro copy_source = {'Bucket': source_bucket,'Key': source_key } s3_resource.Bucket(destination_bucket_name).Object(target_key) .copy(copy_source, ExtraArgs={'ACL': 'bucket-owner-full-control'})
Per il linguaggio Java è sempre possibile usare la libreria SDK all’interno delle Funzioni tuttavia c’è un enorme problema: nel linguaggio Java le lambda function sono blocchi di codice senza nomee invocati con l’operatore freccia ->
, per esempio:
numbers.forEach( (n) -> { System.out.println(n); } );
e purtroppo c’è un caso di omonimia con le Lambda Function del Cloud AWS, la cosa può creare confusione tra i non programmtori ma chi conosce il linguaggio Java comprende sempre la differenza tra i due concetti di funzione. Tipicamente in questo sito si usa sempre Python come linguaggio per le funzioni lambda e per gli esempi combinati con CloudFormation e gli altri servizi, tuttavia è possibile trovare esempi di AWS-Lambda Function scritti nel linguaggio Java nel repository:
https://github.com/alnao/AwsLambdaExamples
Con la libreria SDK è possibile gestire le lambda recuperando l’elenco delle Funzioni Lambda, invocare le funzioni da remoto e creare Lambda Application, concetti che saranno descritti in un prossimo articolo.