Siga las instrucciones paso a paso que se indican a continuación para crear su backend sin servidor. Haga clic en cada uno de los números de paso para ampliar la sección correspondiente.

  • Paso 1. Crear una política de IAM para un rol de IAM personalizado.

    Para obtener permisos para administrar las solicitudes de backend de su sitio web, cree una política de IAM personalizada que otorgue permisos para los siguientes servicios de AWS:

    • Step Functions
      • StartExecution (stateMachine:examplecorp_eao)
      • Sts:AssumeRole
    • Lambda
      • function:examplecorp_eao_createfleet
      • function:examplecorp_eao_createimagebuilder
      • function:examplecorp_eao_getimagename
      • function:examplecorp_eao_getstatus
      • function:examplecorp_eao_sendstreamingurl
      • function:examplecorp_eao_stopresources
      • Sts:AssumeRole
    • AppStream 2.0
      • AssociateFleet (Fleet/*)
      • CreateFleet (Fleet/*)
      • CreateImageBuilder (Image-Builder/*)
      • CreateImageBuilderStreamingURL (Image-Builder/*)
      • CreateStack (Stack/*)
      • CreateStreamingURL (Fleet/*)
      • DescribeFleets (Fleet/*)
      • DescribeImageBuilders (Image-Builder/*)
      • DescribeImages (Image/*)
      • StartFleet (Fleet/*)
      • StartImageBuilder (Image-Builder/*)
      • StopFleet (Fleet/*)
      • StopImageBuilder (Image-Builder/*)
    • SES
      • SendEmail
    • API Gateway
      • Sts:AssumeRole
    • CloudWatch
      • *

    Siga los pasos que se describen a continuación para crear una política de IAM personalizada.

     1. Abra la consola de IAM en https://console.aws.amazon.com/iam/

     2. En el panel de navegación, elija Políticas.

     3. Si es su primera vez eligiendo Políticas, aparece la página Bienvenido a las políticas administradas. Elija Introducción.

     4. Elija Crear política.

     5. Seleccione la pestaña JSON.

     6. Copie y pegue la siguiente política JSON en el campo del documento de política.

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "lambda:InvokeFunction",
                    "appstream:DescribeImages",
                    "appstream:StartFleet",
                    "appstream:StopImageBuilder",
                    "appstream:CreateStack",
                    "appstream:CreateImageBuilderStreamingURL",
                    "appstream:AssociateFleet",
                    "appstream:DescribeImageBuilders",
                    "appstream:StopFleet",
                    "appstream:CreateImageBuilder",
                    "appstream:CreateFleet",
                    "appstream:DescribeFleets",
                    "states:StartExecution",
                    "appstream:StartImageBuilder",
                    "appstream:CreateStreamingURL"
                ],
                "Resource": [
                    "arn:aws:appstream:*:*:fleet/*",
                    "arn:aws:appstream:*:*:image/*",
                    "arn:aws:appstream:*:*:stack/*",
                    "arn:aws:appstream:*:*:image-builder/*",
                    "arn:aws:states:<REGION-CODE>:<AWS-ACCOUNT-ID>:stateMachine:examplecorp_eao",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_sendstreamingurl",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_createimagebuilder",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_getstatus",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_getimagename",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_createfleet",
                    "arn:aws:lambda:<REGION-CODE>:<AWS-ACCOUNT-ID>:function:examplecorp_eao_stopresources"
                ]
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": [
                    "ses:SendEmail",
                    "logs:CreateLogStream",
                    "cloudwatch:*",
                    "logs:CreateLogGroup",
                    "logs:PutLogEvents"
                ],
                "Resource": "*"
            }
        ]
    }
    

     7. Sustituya el <REGION-CODE> con el valor que corresponde a la región de AWS donde implementa el portal de incorporación de la aplicación. El valor debe estar en minúsculas. Para obtener una lista de los código de la región de AWS para AppStream 2.0, consulte la columna Región en la tabla de puntos de enlace de Amazon AppStream 2.0 de la Referencia general de AWS. Sustituya <AWS-ACCOUNT-ID> con su ID de cuenta de AWS.

     8. Cuando termine, elija Revisar la política.

     9. En Nombre, escriba el siguiente nombre para su nueva política: examplecorp_eao_policy.

     10. Elija Crear política.

  • Paso 2. Cree un rol vinculado con el servicio de IAM que permita que las funciones de Lambda llamen a los servicios de AWS.

    Lambda requiere un rol vinculado con el servicio de IAM para que el servicio pueda acceder a los recursos de otros servicios en su nombre. Realice los siguientes pasos para crear un rol vinculado con el servicio de IAM y adjunte la política que creó para este rol.

       1. Abra la consola de IAM en https://console.aws.amazon.com/iam/

       2. En el panel de navegación, en Roles, elija Crear rol.

       3. Para Seleccionar el tipo de entidad de confianza, mantenga el servicio de AWS seleccionado.

       4. Elija Lambda, y luego elija Siguiente: Permisos.

       5. En el cuadro de búsqueda Filtrar políticas, escriba examplecorp_eao_policy. Cuando aparece la política en la lista, seleccione el cuadro de verificación que está junto al nombre de la política.

       6. Elija Siguiente: Etiquetas. Si bien puede especificar una etiqueta para la política, no se requiere una etiqueta.

       7. Elija Siguiente: Revisión.

       8. En Nombre del rol, escriba examplecorp_eao_role.

       9. Elija Crear rol.

       10. En el cuadro de búsqueda, escriba examplecorp_eao_role. Cuando aparece el rol en la lista, seleccione el nombre del rol.

       11. En la parte superior de la página Resumen, aparece el ARN del rol. Tome nota de este ARN. Este ARN se necesita más adelante en el proyecto.

       12. Elija la pestaña Relaciones de confianza y luego elija Editar las relaciones de confianza.

       13. Bajo Documento de la política, configure la política de relación de confianza para incluir la acción sts:AssumeRole para los principales servicios lambda.amazonaws.com, states.amazonaws.com y apigateway.amazonaws.com. El formato debería ser el siguiente:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "Service": [
              "lambda.amazonaws.com",
              “states.amazonaws.com”,
              "apigateway.amazonaws.com"
            ]
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }

    14. Cuando termine, elija Actualizar política de confianza para guardar sus cambios.  

  • Paso 3. Crear y configurar seis funciones de Lambda.

    Siga los pasos que se describen a continuación para crear seis funciones de Lambda.

       1. Abra la consola de Lambda en https://console.aws.amazon.com/lambda/

       2. Aplique alguna de las siguientes acciones:

          • Si no ha creado ninguna función de Lambda, aparece una página de Introducción. En Introducción, elija Crear una función.

          • Si ya ha creado una función de Lambda, en la esquina superior derecha de la página Funciones, elija Crear función.

       3. En la página Crear función, guarde el Autor desde cero seleccionado

       4. En Información básica, haga lo siguiente:

          • Para Nombre, escriba examplecorp_eao_createimagebuilder.

          • Para Tiempo de ejecución, elija Python 3.7.

       5. En Permisos, elija el ícono junto a Elegir o crear un rol de ejecución. Luego, haga lo siguiente:

          • Para Rol de ejecución, elija Usar un rol existente.

          • Para Rol existente, elija examplecorp_eao_role de la lista.

       6. Haga clic en Crear función.

       7. En la sección Código de función, de la pestaña lambda_function, aparece el código de marcador de posición. Borre el código de marcador de posición, y copie y pegue el siguiente código en la pestaña:

    from __future__ import print_function
    import json
    import boto3
    appstream = boto3.client('appstream')
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        # Get variables from web form
        strIBName = event['ib_name']
        strIBARN = event['ib_arn']
        strInstType = event['instance_type']
        strSubnetId = event['subnet1id']
        strSecurityGroups = event['securitygroups']
        strUserEmail = event['email']
        strUserName = event['user_name']
        strUserEmail = strUserEmail.strip()
        strUserName= strUserName.strip()  
    
        try:
            response = appstream.create_image_builder(
                Name=strIBName,
                ImageArn=strIBARN,
                VpcConfig={
                    'SubnetIds':[
                        strSubnetId
                        ],
                    'SecurityGroupIds':[
                        strSecurityGroups
                        ]
                },
                InstanceType=strInstType)
            print("Started: " + strIBName)
            return 1
        except Exception as e:
            print(e)
            message = 'Error creating image builder'
            print(message)
    

       8. Guarde la función. Esta función crea un creador de imágenes de AppStream 2.0.

       9. Repita los pasos del 2 al 8 por cada una de las siguientes funciones de Lambda.
           Nota: En el paso 7, pegue el código de la función de Lambda adecuada.

    Función de Lambda 2: Obtenga el estado del creador de imágenes y flota de AppStream 2.0

    Cuando cree esta función, nómbrela examplecorp_eao_getstatus.

    from __future__ import print_function
    
    import json
    import boto3
    
    print('Loading function')
    appstream = boto3.client('appstream')
    
    def Status_to_Return(received_state):
        switcher = {
        "PENDING": 0,
        "RUNNING": 1,
        "SNAPSHOTTING": 2,
        "STOPPED": 3,
        "STOPPING": 4,
        "STARTING": 5  
        }
        return switcher.get(received_state, -1)
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        # Get names to query against
        strIBName = event['ib_name']
        strFleetName = event['fleet_name']
        strType = event['type']
        try:
            #Fleet status check
            if strType == 2:
                print ("Type is Fleet")
                AS2state = appstream.describe_fleets(Names=[strFleetName])
                Status = AS2state['Fleets'][0]['State']
                print ("State: " + Status)
            #Image Builder status check
            elif strType == 1:
                print ("Type is ImageBuilder")
                AS2state = appstream.describe_image_builders(Names=[strIBName])
                Status = AS2state['ImageBuilders'][0]['State']
                print ("State: " + Status)
            return Status_to_Return(Status)
        except Exception as e:
            print(e)
            print("Error getting status")
            return -1
    

    Función de Lambda 3: Envíe un URL de streaming

    Cuando cree esta función, nómbrela examplecorp_eao_sendstreamingurl.

    Nota: Antes de guardar esta función, busque <REGION-CODE> y sustitúyalo por el código de región que corresponde a la región de AWS en la que configuró SES. Luego, busque Source='noreply@example.com' y cambie este valor por la dirección de correo electrónico que verificó en SES en el módulo anterior 

    from __future__ import print_function
    
    import json
    import boto3
    
    print('Loading function')
    appstream = boto3.client('appstream')
    ses = boto3.client('ses',region_name=<REGION-CODE>)
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        # Get resource names
        strUserName = event['user_name']
        strUserName = strUserName.strip()
        strEmail = event['email']
        strEmail = strEmail.strip()
        strIBName = event['ib_name']
        strFleetName = event['fleet_name']
        strStackName = event['stack_name']
        strType = event['type']
    
        try:
            #Create Fleet Streaming URL
            if strType == 2:
                print ("Type is Fleet")
                mailBody = "Your fleet is now created and ready for verification. Please use the connect link below to connect to the instance."
                url = appstream.create_streaming_url(
                    StackName=strStackName,
                    FleetName=strFleetName,
                    UserId=strUserName,
                    Validity=604800
                )            
            #Create Image Builder Streaming URL
            elif strType == 1:
                print ("Type is ImageBuilder")
                mailBody = "If this is your first time installing your application with AppStream2.0, you can check the getting started guide below for assistance. Otherwise, please click the connect link below to get started."
                url = appstream.create_image_builder_streaming_url(
                    Name=strIBName,
                    Validity=604800
                )
            strStreamingURL = url['StreamingURL']
            print("Streaming URL: " + strStreamingURL)
            #Send Streaming URL in E-mail via SES
            ses.send_email(
                Source='noreply@example.com',
                Destination={
                    'ToAddresses': [strEmail],
                            },
                Message={
                    'Body': {
                    'Html': {
                    'Charset': 'UTF-8',
                    'Data': '<html><head><title>Example Corp</title></head><body bgcolor="#e0e0e0">' + strUserName + ',<p>Thank you for trying out the Example Corp <strong>Enterprise Application Onboarding</strong> demo. ' + mailBody + '</p><p><a href=" ' + strStreamingURL + ' ">Connect</a></p><a href="https://d1.awsstatic.com/product-marketing/AppStream2.0/Amazon%20AppStream%202.0%20Getting%20Started%20Guide%20April%202018.pdf">Getting Started Guide</a></body></html>',
                    },
                    },
                    'Subject': {
                    'Charset': 'UTF-8',
                    'Data': 'Example Corp Enterprise Application Onboarding for ' + strUserName                    
                    },
                }
            )
            return 1
    
        except Exception as e:
            print(e)
            print("Error getting streaming URL")
            return -1
    

    Función de Lambda 4: Obtenga el nombre de la imagen de AppStream 2.0

    Cuando cree esta función, nómbrela examplecorp_eao_getimagename.

    from __future__ import print_function
    
    import json
    import boto3
    import datetime
    
    print('Loading function')
    appstream = boto3.client('appstream')
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        # Get names to query against
        strIBName = event['ib_name']
        try:
            ImagesCMD = appstream.describe_images(Type='PRIVATE')
            Images = ImagesCMD['Images']
            for Image in Images:
                if Image.get('ImageBuilderName','') == strIBName:
                    ImageTime = Image['CreatedTime']
                    now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
                    howold = now - ImageTime
                    HowOldSec = howold.total_seconds()
                    if HowOldSec < 3600:
                        imagename = Image['Name']
                        return (imagename)
        except Exception as e:
            print(e)
            print("Error getting Image Name")
            return -1
    

    Función de Lambda 5: Cree una flota y una pila de AppStream, y asocie la flota con una pila

    Cuando cree esta función, nómbrela examplecorp_eao_createfleet.

    from __future__ import print_function
    import json
    import boto3
    print('Loading function')
    appstream = boto3.client('appstream')
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        strFleetName = event['fleet_name']
        strStackName = event['stack_name']
        strImageName = event['imagename']
        strInstType = event['instance_type']
        strSubnet1Id = event['subnet1id']
        strSubnet2Id = event['subnet2id']
        strSecurityGroups = event['securitygroups']
    
        try:
            #Create AppStream fleet
            fleet = appstream.create_fleet(
                Name=strFleetName,
                ImageName=strImageName,
                ComputeCapacity={
                    'DesiredInstances': 1
                },            
                VpcConfig={
                    'SubnetIds':[
                        strSubnet1Id,
                        strSubnet2Id
                        ],
                    'SecurityGroupIds':[
                        strSecurityGroups
                        ]
                },
                InstanceType=strInstType)
            #Create AppStream stack
            stack = appstream.create_stack(
                Name=strStackName,
                StorageConnectors=[
                    {
                        'ConnectorType': 'HOMEFOLDERS'
                    },
                ],
                UserSettings=[
                    {
                        'Action': 'CLIPBOARD_COPY_FROM_LOCAL_DEVICE',
                        'Permission': 'ENABLED'
                    },
                ],
                ApplicationSettings={
                    'Enabled': False
                }
            )
            #Associate Stack to Fleet
            associate = appstream.associate_fleet(
                FleetName=strFleetName,
                StackName=strStackName
            )
            startfleet = appstream.start_fleet(
                Name=strFleetName
            )
            return 2
        except Exception as e:
            print(e)
            print("Error creating fleet")
            return -1
    

    Función de Lambda 6: Detenga la flota y el creador de imágenes de AppStream 2.0

    Cuando cree esta función, nómbrela examplecorp_eao_stopresources.

    from __future__ import print_function
    
    import json
    import boto3
    
    print('Loading function')
    appstream = boto3.client('appstream')
    
    def Status_to_Return(received_state):
        switcher = {
        "PENDING": 0,
        "RUNNING": 1,
        "SNAPSHOTTING": 2,
        "STOPPED": 3,
        "STOPPING": 4
        }
        return switcher.get(received_state, -1)
    
    def lambda_handler(event, context):
        # Log the received event
        print("Received event: " + json.dumps(event, indent=2))
        # Get names to query against
        strIBName = event['ib_name']
        strFleetName = event['fleet_name']
        strType = event['type']
        try:
            #Fleet status check
            if strType == 2:
                print ("Type is Fleet")
                AS2state = appstream.describe_fleets(Names=[strFleetName])
                Status = AS2state['Fleets'][0]['State']
                State = Status_to_Return(Status)
                if State < 3:            
                    print (strFleetName + " is " + Status + " attempting to stop")
                    AS2stop = appstream.stop_fleet(Name=strFleetName)
                print ("State: " + Status)
            #Image Builder status check
            elif strType == 1:
                print ("Type is ImageBuilder")
                AS2state = appstream.describe_image_builders(Names=[strIBName])
                Status = response['ImageBuilders'][0]['State']
                State = Status_to_Return(Status)
                if State < 3:
                    print (strIBName + " is " + Status + " attempting to stop")
                    AS2stop = appstream.stop_image_builder(Names=[strIBName])
                print ("State: " + Status)
            return Status_to_Return(Status)
        except Exception as e:
            print(e)
            print("Error stopping services")
            return -1