Automatización para la creación de imágenes con Amazon EC2 Image Builder
Tags: configuration, imagebuilder, ami, security
Published: 2024-03-25 15:36:38


By: Eliana Perez
Si tienes que desplegar o administrar imágenes de máquinas virtuales con EC2 Image Builder desde novato a experto, este tutorial te va a servir. ¡Guárdalo!

##Builder nivel 100
¿Qué es una AMI?
Una AMI (Amazon Machine Image) es una plantilla de virtualización utilizada para crear instancias de máquinas virtuales (VM) en Amazon Web Services (AWS). Una AMI proporciona la información necesaria para lanzar una instancia EC2 (Elastic Compute Cloud) que puede ser una máquina virtual de Windows o Linux, junto con el software preinstalado, configuraciones del sistema o personalizadas y datos asociados.

Con las AMIs podremos desplegar entornos de computación en la nube de manera rápida y consistente. 

Hay AMIs ofrecidas por AWS y podemos crear AMIs propias.
En este video se muestra como lanzar una instancia de EC2 con una AMI personalizada. Es el mismo proceso para imágenes de Amazon o de terceros.
https://www.youtube.com/watch?v=Kg1XRjOHqFY

En este video se muestra como lanzar una instancia EC2 con AMI de Windows
https://www.youtube.com/watch?v=DshwCN2twuE 

##Builder nivel 200

Inicialmente la gestión y actualización de imágenes puede realizarse de forma manual. Pero bien podría automatizarse este proceso utilizando el creador de imágenes EC2 que simplifica la creación, el mantenimiento, la validación, el intercambio y la implementación de imágenes de Linux o Windows para usarlas con Amazon EC2 y en las premisas. Así habrá mayor productividad, soporte integrado y conformidad con la postura de seguridad de la organización.

Este tutorial técnico ayudará con la automatización de la creación de imágenes utilizando EC2 Image Builder
https://www.youtube.com/watch?v=YP2hPUq-VAc&t=15s 

Este workshop te llevará paso a paso en la automatización para creación de imágenes en Windows. Aplica igualmente para Linux
https://catalog.us-east-1.prod.workshops.aws/workshops/d6c7ecdc-c75f-4ad1-910f-fdd994cc4aed/en-US 

###Builder nivel 300 
**Caso de Uso**
El equipo de seguridad, libera una nueva imagen (AMI) con las últimas actualizaciones de seguridad para los sistemas operativos Windows y Linux.
De forma automatizada debe crearse una nueva AMI que, a partir de la AMI entregada por el equipo de seguridad, agregue datos, software pre-instalado y configuraciones de sistema o personalizada, para posteriormente activar el despliegue de un nuevo pipeline que cree las nuevas instancias, con la nueva configuración y apague las instancias que ya no son conformes. 
El objetivo es que el equipo de pruebas trabaje en un sistema operativo conforme con la postura de seguridad de la organización, sin perder eficiencia en los recursos, productividad en los equipos o elevar los costos.

**Solución**
Cada vez que se genera una nueva actualización del equipo de seguridad, se detectará el evento que viene a través de Amazon Eventbridge, automáticamente se creará una nueva versión de la imagen (ya conforme y con las configuraciones necesarias) con EC2 Image Builder y se notificará al pipeline que activará la creación de las nuevas instancias de EC2 sobre las que debe trabajar el equipo de pruebas. Se notificará del proceso a través del canal de slack correspondiente con AWS chatbot.

**Arquitectura de la solución**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v99mi00yh4alf0os3mtu.jpg)


Amazon Eventbridge es un enrutador de eventos que se encarga en este caso de llevar a quien lo solicite la información de que una nueva AMI de seguridad está disponible. Para ello se debe crear la siguiente regla.
En este contexto solo el equipo de seguridad crea las AMIs

```
1.	{
2.	"source": ["aws.ec2"],
3.	"detail-type": ["EC2 AMI State Change"],
4.	"detail": {
5.	"State": ["available"]
6.	}
7.	}

```

Una Función Lambda se encargará de procesar el evento. Básicamente, una función Lambda es una pieza de código que se ejecuta en la nube de AWS en respuesta a eventos específicos, como cambios en la creación de AMIs, cambio en los datos, solicitudes HTTP, actualizaciones de bases de datos, cargas de archivos, etc. Puedes escribir funciones Lambda en varios lenguajes de programación, como Python, Node.js, Java, C#, entre otros.
La función lambda recibe como entrada el evento que se generó desde Eventbridge con el siguiente payload

```
1.	{
2.	"version": "0",
3.	"id": "123f456f-7b89-c012-d3cb-e4567de8901e",
4.	"detail-type": "EC2 AMI State Change",
5.	"source": "aws.ec2",
6.	"account": "123456789012",
7.	"time": "2024-01-09T14:46:28Z",
8.	"region": "us-east-x",
9.	"resources": [
10.	"arn:aws:ec2:us-east-x::image/ami-1c2df3456fe78901b"
11.	],
12.	"detail": {
13.	"RequestId": "1a2c345a-fdd6-78b9-01bf-2eedb345678b",
14.	"State": "available",
15.	"ImageId": "ami-1c2df3456fe78901b",
16.	"ErrorMessage": ""
17.	}
18.	}

```

La función lambda está hecha en Python por lo que utilizaremos la librería boto3 v 1.34.69. Utilizas el SDK de AWS para Python (Boto3) para crear, configurar y gestionar servicios de AWS, como Amazon Elastic Compute Cloud (Amazon EC2) y Amazon Simple Storage Service (Amazon S3). El SDK proporciona una API orientada a objetos, así como acceso de bajo nivel a los servicios de AWS. Aquí está el enlace que te llevará a la documentación de EC2 Image Builder
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/imagebuilder.html 

**Retomando EC2 Image Builder**
Un pipeline de imagen para automatizar la creación de AMIs, necesita al menos una Receta de Imagen y una Configuración de Infraestructura. Las recetas de Imagen son un recurso versionado que contiene una imagen Padre y unos componentes.
La versión de la receta de imagen debemos obtenerla en la función lambda
La imagen padre es la imagen enviada por el equipo de seguridad de la que debemos tener su ARN.
Los componentes son los bloques de construcción que son consumidos por una receta de imagen, por ejemplo, paquetes para instalación, pasos de endurecimiento de seguridad y pruebas.
Son básicamente 3 pasos


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et7uaix3ecv30914p4gw.png)


Una receta de imagen es un documento que define los componentes que se van a aplicar a las imágenes base para crear la configuración deseada para la imagen de salida. Luego de crear la receta, no es posible modificarla. Se debe crear una nueva versión para cambiar los componentes.
Los componentes de compilación y las pruebas forman parte de una receta. Los componentes de compilación contienen software, ajustes y configuraciones que se instalan o aplican durante el proceso de creación de imágenes personalizadas. Las pruebas se ejecutan después de crear una imagen personalizada para validar la funcionalidad, la seguridad, el rendimiento, etc.
 
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/788hpztkhd9xsoers12t.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mdesw66ck2d93ul7fti8.png)


La creación de estos componentes también puede automatizarse utilizando AWS CloudFormation y entregarlos a la función Lambda como variables de entorno. AWS CloudFormation es un servicio de infraestructura como código (IaC) que te permite modelar, aprovisionar y gestionar fácilmente recursos de AWS y de terceros.
Aquí un ejemplo de como crear una carpeta. Se define como componente, se le da un nombre un sistema operativo, descripción, versión, pasos y acciones a tomar para la creación de esa carpeta.


```
CreateFolderComponent:
    Type: 'AWS::ImageBuilder::Component'
    Properties:
      Name: 'folderssTestingHubCreation'
      Platform: 'Linux'
      Version: '1.0.2'
      Description: 'This component create a folder called TestingHub in the ubuntu user folder and delete the .m2 folder'
      SupportedOsVersions: 
        - 'Ubuntu 20'
      # Require one of 'Data' or 'Uri' for Component template
      Data: |
        name: CreationOfTestingHubFolder
        description: This is to create TestingHub folder on the root path
        schemaVersion: 1.0

        phases:
          - name: build
            steps:
            
              - name: CreatingFolderLinux
                action: CreateFolder
                inputs:
                  - path: /home/ubuntu/TestingHubDeepak3

              - name: DeletingFolderM2
                action: DeleteFolder
                inputs:
                  - path: /home/ubuntu/.m2
                    force: true
```


Dado que la función lambda donde se va a procesar el evento es stateless, se debe guardar la información de la versión de la receta de imagen en un sitio seguro. AWS proporciona ese sitio en el servicio AWS System Manager Parameter store. El Parameter Store se utiliza comúnmente para almacenar datos de configuración que necesitan ser compartidos entre diferentes servicios o instancias de EC2, como configuraciones de aplicaciones, credenciales, rutas de conexión a bases de datos, etc. Proporciona integraciones con otros servicios de AWS, como AWS Lambda, Amazon ECS, Amazon EC2, entre otros, lo que facilita el acceso seguro a los datos de configuración desde diferentes partes de tu infraestructura.
Ya con los recursos necesarios para crear la receta de imagen procedemos a crear la función Lambda

**Lógica de la función Lambda**

1.	Se recibe el evento
2.	Si el nombre de la ami que esta disponible comienza con un prefijo especifico continuamos
3.	Se carga en la función la lista de componentes que van a ejecutarse en la receta de imagen
4.	Se obtiene y actualiza la ultima versión de la receta de Imagen
5.	Se actualiza esta nueva versión en el pipeline de EC2 Image Builder
6.	Se crea la nueva imagen
7.	Se activa el pipeline de creación de la nueva receta de imagen
8.	Inicia el proceso de notificación con aws chatbot y publica el tópicon con SNS
	
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1qmnwlu33emvqbpbv8x.png)


Cuando se actualiza el ID de la nueva AMI, el pipeline que despliega las instancias EC2 ya conformes, detecta el evento a través de una regla de Eventbridge, activa la ejecución del Pipeline, se crean las nuevas instancias, se apagan las que ya estaban y ya el equipo de pruebas puede continuar sin problema.
Con esta automatización se puede reducir a horas la aplicación de políticas de conformidad, así como la carga operativa necesaria para mantener la postura de seguridad de la organización.

**Función Lambda**

```
import json
import boto3
import os



#Boto3 Clients
imageBuilder = boto3.client('imagebuilder')
ec2 = boto3.client('ec2')
ssm = boto3.client('ssm')

#environment variables

testRecipeArn= os.environ['testRecipeArn']
infrastructureConfigurationArn= os.environ['infrastructureConfigurationArn'] 
imagepipelineArn = os.environ['imagepipelineArn']
ssmPath = os.environ['ssmPath']
components= os.environ['components']


def lambda_handler(event, context):
    # Image Id delivers by Event Bridge event
    amiId= event['detail']['ImageId']

    # Get the name associated with the Ami ID
    amiInformation= ec2.describe_images(ImageIds=[ amiId, ],)
    amiName= amiInformation['Images'][0]['Name']

      # Compare if the name Ami starts with the specific pattern
    if amiName.startswith('testRecipe') :
        try:
            print('Correct Image')

            #create components array

            componentsRecipe= components.split(',')
            component = [{"componentArn": arn} for arn in componentsRecipe]
            
            print(component)

            # get Actual version
            version = ssm.get_parameter(Name=ssmPath)['Parameter']['Value']
            values = version.split('.')
            nextVersion = int(values[2]) + 1

            # Convert nextVersion to string before concatenating
            newValueVersion = values[0]+"."+ values[1]+"."+ str(nextVersion)

            print(newValueVersion)

            #update new value version
            ssm.put_parameter(Name=ssmPath, Value=newValueVersion, Type="String", Overwrite=True)


            # Create the new Image Recipe with the new AMI 
            newImageRecipe = imageBuilder.create_image_recipe(
                name='testRecipe',
                description='newversion' + newValueVersion,
                semanticVersion=newValueVersion,
                components=component,
                parentImage=amiId
            )
            # Get the new recipe Arn
            newImageRecipeArn = newImageRecipe['imageRecipeArn']

            # Update the Image Pipeline with the new Image Recipe
            response = imageBuilder.update_image_pipeline(
                imagePipelineArn=imagepipelineArn,
                description='new version for imagepipeline' + newValueVersion,
                imageRecipeArn=newImageRecipeArn,
                infrastructureConfigurationArn=infrastructureConfigurationArn
            )

        except Exception as e:
            print("Error:", e)
```

Template CloudFormation para automatizar todo el proceso

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'


Resources:
  # Create an S3 Bucket for logs.
  # When deleting the stack, make sure to empty the bucket first.
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html
  ImageBuilderLogBucket:
    Type: AWS::S3::Bucket
    # If you want to delete the stack, but keep the bucket, set the DelectionPolicy to Retain.
    # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
    # DeletionPolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        IgnorePublicAcls: true
        BlockPublicPolicy: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled

  # By default, AWS Services do not have permission to perform actions on your instances. This grants
  # AWS Systems Manager (SSM) and EC2 Image Builder the necessary permissions to build an image.
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  # https://docs.aws.amazon.com/imagebuilder/latest/userguide/image-builder-setting-up.html
  InstanceRole:
    Type: AWS::IAM::Role
    Metadata:
      Comment: Role to be used by instance during image build.
    Properties:
      ManagedPolicyArns:
        - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore
        - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/EC2InstanceProfileForImageBuilder
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - !Sub 'ec2.${AWS::URLSuffix}'
        Version: '2012-10-17'
      Path: /executionServiceEC2Role/

  # Policy to allow the instance to write to the S3 bucket (via instance role / instance profile).
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html
  InstanceRoleLoggingPolicy:
    Type: AWS::IAM::Policy
    Metadata:
      Comment: Allows the instance to save log files to an S3 bucket.
    Properties:
      PolicyName: ImageBuilderLogBucketPolicy
      Roles:
        - Ref: InstanceRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - s3:PutObject
            Effect: Allow
            Resource:
              - Fn::Sub:
                  - arn:${AWS::Partition}:s3:::${BUCKET}/*
                  - BUCKET:
                      Ref: ImageBuilderLogBucket

  # To pass the InstanceRole to an EC2 instance, we need an InstanceProfile.
  # This profile will be used during the image build process.
  # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /executionServiceEC2Role/
      Roles:
        - Ref: InstanceRole

  # Specifies the infrastructure within which to build and test your image.
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-infrastructureconfiguration.html
  Ubuntu2004ImageInfrastructureConfiguration:
    Type: AWS::ImageBuilder::InfrastructureConfiguration
    Properties:
      InstanceTypes:
        - 'm4.large'
        - 'm5.large'
      Name: Ubuntu-2004-with-latest-SSM-Agent-Infrastructure-Configuration
      InstanceProfileName:
        Ref: InstanceProfile
      # Specify an S3 bucket and EC2 Image Builder will save logs to the bucket.
      Logging:
        S3Logs:
          S3BucketName:
            Ref: ImageBuilderLogBucket
          # S3KeyPrefix: 'my-imagebuilder-bucket-prefix'
      # If you would like to keep the instance running after a failed build, set TerminateInstanceOnFailure to false.
      # TerminateInstanceOnFailure: false
      # If you do not have a default VPC or want to use a different VPC, you must specify the subnet ID to use
      # SubnetId: 'subnet-id'

  # The CloudWatch LogGroup for the image build logs is provided to ensure the LogGroup is cleaned up if the stack is deleted.
  Ubuntu2004LogGroup:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Properties:
      LogGroupName: /aws/imagebuilder/Ubuntu-2004-with-latest-SSM-Agent
      RetentionInDays: 3

  # Recipe which references the latest (x.x.x) version of the Amazon Linux 2 AMI).
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html
  Ubuntu2004ImageRecipe:
    Type: AWS::ImageBuilder::ImageRecipe
    Properties:
      Name: Ubuntu-2004-with-latest-SSM-Agent
      Version: 1.0.1
      # ${AWS::Partition} returns the partition where you are running the CloudFormation template. For standard AWS regions, the
      # partition is aws. For resources elsewhere, the partition is aws-partitionname. For example, China (Beijing and Ningxia)
      # regions use aws-cn and AWS GovCloud (US) regions are aws-us-gov.
      # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html
      ParentImage:
        Fn::Sub: arn:${AWS::Partition}:imagebuilder:${AWS::Region}:aws:image/ubuntu-server-20-lts-x86/x.x.x
      Components:
        - ComponentArn:
            Fn::Sub: arn:aws:imagebuilder:${AWS::Region}:aws:component/amazon-cloudwatch-agent-linux/x.x.x
        - ComponentArn: 
            Fn::Sub: arn:aws:imagebuilder:${AWS::Region}:aws:component/amazon-corretto-11-apt-generic/x.x.x
        - ComponentArn:
            Fn::Sub: arn:aws:imagebuilder:${AWS::Region}:aws:component/powershell-linux/x.x.x
        - ComponentArn: !Ref CreateFolderComponent
        - ComponentArn: !Ref MavenInstallationComponent

  # Create an SSM Parameter Store entry with our resulting ImageId.
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-parameter.html

  Ubuntu2004WithLatestSSMAgentParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Description: image recipe latest version for image build pipeline 
      Name: /ec2ImageBuilder/imageRecipeVersion
      Type: String
      Value: '0.0.1'

  ImagePipelineBami:
    Type: 'AWS::ImageBuilder::ImagePipeline'
    Properties:
      Name: 'golden-image-for-BAMI'
      Description: 'description'
      ImageRecipeArn: !Ref Ubuntu2004ImageRecipe
      InfrastructureConfigurationArn: !Ref Ubuntu2004ImageInfrastructureConfiguration
      ImageTestsConfiguration:
        ImageTestsEnabled: false
        TimeoutMinutes: 90

  CreateFolderComponent:
    Type: 'AWS::ImageBuilder::Component'
    Properties:
      Name: 'folderssTestingHubCreation'
      Platform: 'Linux'
      Version: '1.0.2'
      Description: 'This component create a folder called TestingHub in the ubuntu user folder and delete the .m2 folder'
      SupportedOsVersions: 
        - 'Ubuntu 20'
      # Require one of 'Data' or 'Uri' for Component template
      Data: |
        name: CreationOfTestingHubFolder
        description: This is to create TestingHub folder on the root path
        schemaVersion: 1.0

        phases:
          - name: build
            steps:
            
              - name: CreatingFolderLinux
                action: CreateFolder
                inputs:
                  - path: /home/ubuntu/TestingHubDeepak3

              - name: DeletingFolderM2
                action: DeleteFolder
                inputs:
                  - path: /home/ubuntu/.m2
                    force: true

  MavenInstallationComponent:
    Type: 'AWS::ImageBuilder::Component'
    Properties:
      Name: 'MavenComponent'
      Platform: 'Linux'
      Version: '1.0.0'
      Description: 'This component is to install maven'
      SupportedOsVersions: 
        - 'Ubuntu 20'
      # Require one of 'Data' or 'Uri' for Component template
      Data: |
        name: Maven
        description: This is to install Maven
        schemaVersion: 1.0
        phases:
          - name: build
            steps:
              - name: Install_Maven
                action: ExecuteBash
                inputs:
                  commands:
                    - sudo apt update
                    - sudo apt install maven -y

## Lambdas Event Management 

  StartImagePipelineLambda:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: startImagePipeline.lambda_handler
      Runtime: python3.8
#      CodeUri: ../functions/startImagePipeline
      InlineCode: |
        import json
        import boto3
        import os



        #Boto3 Clients
        imageBuilder = boto3.client('imagebuilder')
        ec2 = boto3.client('ec2')
        ssm = boto3.client('ssm')

        #environment variables

        testRecipeArn= os.environ['testRecipeArn']
        infrastructureConfigurationArn= os.environ['infrastructureConfigurationArn'] 
        imagepipelineArn = os.environ['imagepipelineArn']
        ssmPath = os.environ['ssmPath']
        components= os.environ['components']


        def lambda_handler(event, context):
            # Image Id delivers by Event Bridge event
            amiId= event['detail']['ImageId']

            # Get the name associated with the Ami ID
            amiInformation= ec2.describe_images(ImageIds=[ amiId, ],)
            amiName= amiInformation['Images'][0]['Name']

              # Compare if the name Ami starts with the specific pattern
            if amiName.startswith('testRecipe') :
                try:
                    print('Correct Image')

                    #create components array

                    componentsRecipe= components.split(',')
                    component = [{"componentArn": arn} for arn in componentsRecipe]

                    # get Actual version
                    version = ssm.get_parameter(Name=ssmPath)['Parameter']['Value']
                    values = version.split('.')
                    nextVersion = int(values[2]) + 1

                    # Convert nextVersion to string before concatenating
                    newValueVersion = values[0]+"."+ values[1]+"."+ str(nextVersion)

                    print(newValueVersion)

                    #update new value version
                    ssm.put_parameter(Name=ssmPath, Value=newValueVersion, Type="String", Overwrite=True)

                    # Delete the last 5 characters
        #            modifyVersionImageRecipe = testRecipeArn[:-5]
        #            actualVersionImageRecipe = modifyVersionImageRecipe + version
                    

                    # Get the last Image Recipe components 
        #            getImageRecipe= imageBuilder.get_image_recipe(imageRecipeArn=actualVersionImageRecipe)
        #            components= getImageRecipe['imageRecipe']['components']

                    # Create the new Image Recipe with the new AMI 
                    newImageRecipe = imageBuilder.create_image_recipe(
                        name='testRecipe',
                        description='newversion' + newValueVersion,
                        semanticVersion=newValueVersion,
                        components=component,
                        parentImage=amiId
                    )
                    # Get the new recipe Arn
                    newImageRecipeArn = newImageRecipe['imageRecipeArn']

                    # Update the Image Pipeline with the new Image Recipe
                    response = imageBuilder.update_image_pipeline(
                        imagePipelineArn=imagepipelineArn,
                        description='new version for imagepipeline' + newValueVersion,
                        imageRecipeArn=newImageRecipeArn,
                        infrastructureConfigurationArn=infrastructureConfigurationArn
                    )

        #        
        #            response = imageBuilder.start_image_pipeline_execution(
        #                imagePipelineArn='arn:aws:imagebuilder:us-east-1:012983038368:image-pipeline/golden-image-for-bami'
        #            )
        #            print("Image pipeline execution started successfully.")
        #            print("Execution ID:", response.get('clientToken'))
                except Exception as e:
                    print("Error:", e)
      Description: Lamdba function to trigger Image Build when a new DAMI is created 
      Environment:
        Variables:
          testRecipeArn: !Ref 'Ubuntu2004ImageRecipe'
          infrastructureConfigurationArn: !Ref 'Ubuntu2004ImageInfrastructureConfiguration'
          imagepipelineArn: !Ref 'ImagePipelineBami'
          ssmPath: !Ref 'Ubuntu2004WithLatestSSMAgentParameter'
          components: 
            Fn::Sub: arn:aws:imagebuilder:${AWS::Region}:aws:component/amazon-cloudwatch-agent-linux/x.x.x,arn:aws:imagebuilder:${AWS::Region}:aws:component/amazon-corretto-11-apt-generic/x.x.x,${CreateFolderComponent.Arn},${MavenInstallationComponent.Arn}


      MemorySize: 128
      Timeout: 300
      Role: !GetAtt 'StartImagePipelineRole.Arn'


  StartImagePipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
      Policies:
        - PolicyName: StartImagePipelineLambdaPolicy
          PolicyDocument:
            Statement:
              - Effect: Allow
                Resource: "*"
                Action:
                  - 'ssm:DescribeParameters'
                  - 'ssm:GetParameter'
                  - 'ssm:PutParameter'
                  - 'ssm:GetParameters'
              - Effect: Allow
                Resource: "*"
                Action: 
                  - 'ec2:DescribeImages'
              - Effect: Allow
                Action:
                  - 'imagebuilder:GetWorkflow'
                  - 'imagebuilder:GetComponent'
                  - 'imagebuilder:TagResource'
                  - 'imagebuilder:GetImagePipeline'
                  - 'imagebuilder:GetImageRecipe'
                  - 'imagebuilder:CreateImageRecipe'
                  - 'imagebuilder:GetContainerRecipe'
                  - 'imagebuilder:GetDistributionConfiguration'
                  - 'imagebuilder:GetImage'
                  - 'iam:PassRole'
                  - 'iam:CreateServiceLinkedRole'
                  - 'imagebuilder:GetInfrastructureConfiguration'
                  - 'imagebuilder:UpdateImagePipeline'
                Resource: "*"

  AmiAvailableEventRule:
    Type: AWS::Events::Rule
    Properties:
      Description: "Event rule that captures when a new AMI is available"
      State: "ENABLED"
      EventPattern:
        source:
          - aws.ec2
        detail-type:
          - EC2 AMI State Change
        detail:
          state:
            - available
      Targets:
        - Arn: !GetAtt 'StartImagePipelineLambda.Arn'
          Id: !Ref 'StartImagePipelineLambda'

  SlackNotificationJavaPipelinepEventsRulePermissions:
      Type: AWS::Lambda::Permission
      Properties:
        FunctionName: !Ref 'StartImagePipelineLambda'
        Action: lambda:InvokeFunction
        Principal: events.amazonaws.com
        SourceArn: !GetAtt 'AmiAvailableEventRule.Arn'

######## AWS CHATBOT NOTIFICATIONS RESOURCES #########

  AwsChatbotNotification:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: codePipelineStartnotificationJavaCode
      IamRoleArn: !GetAtt 'AwsChatBotIamRole.Arn'
      LoggingLevel: 'ERROR'
      SlackChannelId: "xxxxxxxxxxxx" #!Ref SlackChannelId
      SlackWorkspaceId: "xxxxxxxxxx" #!Ref SlackWorkspaceId
      SnsTopicArns: 
        - !Ref 'AwsChatbotSnsTopicLambda'
        
  AwsChatbotSnsTopicLambda:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: AwsChatbotSnsTopicLambda
      KmsMasterKeyId:
        Ref: SNSKmsAlias
        
  SNSKmsAlias:
    Type: 'AWS::KMS::Alias'
    Properties:
      AliasName:
        'Fn::Join':
          - '-'
          - - alias/SnsServiceKmsKey
            - 'Fn::Sub': '${AWS::StackName}'
      TargetKeyId:
        Ref: SNSKmsKey
  
  SNSKmsKey:
    Type: 'AWS::KMS::Key'
    Properties:
      Description: This is used to encrypt-decrypt the SNS.
      EnableKeyRotation: 'true'
      KeyPolicy:
        Statement:
          - Action:
              - 'kms:Create*'
              - 'kms:Describe*'
              - 'kms:Enable*'
              - 'kms:List*'
              - 'kms:Put*'
              - 'kms:Update*'
              - 'kms:Revoke*'
              - 'kms:Disable*'
              - 'kms:Get*'
              - 'kms:Delete*'
              - 'kms:ScheduleKeyDeletion'
              - 'kms:CancelKeyDeletion'
              - 'kms:UntagResource'
              - 'kms:TagResource'
            Effect: Allow
            Principal:
              AWS:
                - 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root'
            Resource: '*'
            Sid: Allow access for Key Administrators
          - Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
            Effect: Allow
            Principal:
              AWS:
                - 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root'
                - 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/imagebuilder.amazonaws.com/AWSServiceRoleForImageBuilder'
              Service: cloudwatch.amazonaws.com
            Resource: '*'
            Sid: Allow use of the key
          - Action:
              - 'kms:CreateGrant'
              - 'kms:ListGrants'
              - 'kms:RevokeGrant'
            Condition:
              Bool:
                'kms:GrantIsForAWSResource': true
            Effect: Allow
            Principal:
              AWS:
                - 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root'
            Resource: '*'
            Sid: Allow attachment of persistent resources
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: 
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey*'
            Resource: '*'
          - Action:
              - 'kms:GenerateDataKey*'
              - 'kms:Decrypt'
            Condition:
              StringEquals:
                'kms:ViaService': sns.us-east-1.amazonaws.com
            Effect: Allow
            Principal:
              Service: codestar-notifications.amazonaws.com
            Resource: '*'
            Sid: Allow codepiple to push message to encrypted sns
        Version: '2012-10-17'

  EventTopicPolicy:
    Type: 'AWS::SNS::TopicPolicy'
    Properties:
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - events.amazonaws.com
                - codestar-notifications.amazonaws.com
            Action: 
              - 'sns:Publish'
            Resource: !Ref AwsChatbotSnsTopicLambda
      Topics:
        - !Ref AwsChatbotSnsTopicLambda

  AwsChatBotIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
      - PolicyName: AwsChatBotIamPolicyName
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Action:
                - s3:PutObject*
              Resource:
                - arn:aws:s3:::bucket-name/*
              Effect: Allow
              Condition:
                StringLike:
                  s3:x-amz-acl: bucket-owner-full-control
            - Action:
                - s3:GetBucketAcl
              Resource:
                - arn:aws:s3:::bucketname
              Effect: Allow
            - Effect: Allow
              Action:
                - kms:Encrypt
                - kms:Decrypt
                - kms:ReEncrypt*
                - kms:GenerateDataKey*
                - kms:DescribeKey
              Resource:
                - !GetAtt SNSKmsKey.Arn

```

Espero te sea de mucha utilidad! Disfrutalo!