BLOG / how-its-done

Desplegar fácilmente Skills de Alexa en Amazon Web Services

Marian C. Moldovan el AWSserverlessAlexa Skills

Desplegar fácilmente Skills de Alexa en Amazon Web Services

Si quieres desarrollar una Skill de Alexa como “Mi Héroe a la Batalla”, necesitas hospedar en una infraestructura la lógica que da vida a la conversación. Por la facilidad de integración y por los beneficios de las tecnologías serverless nuestra recomendación es utilizar AWS Lambda.

La infraestructura típica de tu Skill será un entorno de ejecución Lambda, con S3 para guardar y recuperar ficheros tales como sonidos y DynamoDB para el estado de la conversación y los datos necesarios para la lógica de nuestra aplicación.

Si no quieres crear estos recursos a mano te recomendamos que uses alguna herramienta para automatizar la tarea, como CloudFormation o su versión simplificada, SAM.

En este post, vamos a ver qué son, cómo se utilizan y las ventajas de una herramienta frente a la otra para la creación de los servicios necesarios para ejecutar y hospedar con éxito una Skill.

Arquitectura

¿Qué es y cómo funciona CloudFormation?

AWS CloudFormation es un servicio para crear, actualizar y eliminar recursos en la nube de manera automática. Permite definir mediante plantillas YAML o JSON configuraciones para poder replicar el modelo después. En el caso de las Skills es un caso de uso ideal, ya que la arquitectura será siempre la misma, con los mismo componentes, los parámetros variables son nombres, variables de entorno, región, etc. Pero los recursos básicos son los mismos, Lambda y DynamoDB.

Aquí puedes ver un ejemplo de plantilla de CloudFormation para una Skill:

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  SkillName:
    Type: String
    Description: The name of the Alexa Skill
  AppId:
    Type: String
    Description: The app id of your Alexa Skillundefined you can get it from the ask console
  Stage:
    Type: String
    Description: Stage of the infrastructure
    Default: dev
    AllowedValues:
      - dev
      - pro

Resources:
  AlexaFunctionTrigger:
    Type: AWS::Lambda::Permission
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !GetAtt AlexaFunction.Arn
      Principal: alexa-appkit.amazon.com
      EventSourceToken: !Ref AppId
  LambdaRole:
    Description: Creating service role in IAM for AWS Lambda
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub 'role-ask-${Stage}-${SkillName}'
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: !Sub 'policy-ask-dynamo-${Stage}-${SkillName}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'dynamodb:DeleteItem'
                  - 'dynamodb:GetItem'
                  - 'dynamodb:PutItem'
                  - 'dynamodb:Query'
                  - 'dynamodb:Scan'
                  - 'dynamodb:UpdateItem'
                Resource: !GetAtt
                  - AttributesTable
                  - Arn
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
  AlexaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub 'ask-${Stage}-${SkillName}'
      Handler: 'index.handler'
      MemorySize: 128
      Role: !GetAtt
        - LambdaRole
        - Arn
      Timeout: 7
      Runtime: 'nodejs8.10'
      Code:
        ZipFile: !Sub |
          exports.handler = function(eventundefined context) {
             context.done()
          }
  AttributesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub 'ask-${Stage}-${SkillName}'
      BillingMode: 'PAY_PER_REQUEST'
      AttributeDefinitions:
        -
          AttributeName: 'id'
          AttributeType: 'S'
      KeySchema:
        -
          AttributeName: 'id'
          KeyType: HASH
Outputs:
    AlexaFunction:
        Description: "Alexa Lambda Function ARN"
        Value: !GetAtt AlexaFunction.Arn

En la plantilla podemos encontrar:

  • Parámetros. Son las variables de nuestra plantilla entre las que tenemos: nombre de la Skill, AppId de Alexa y el entorno de desarrollo (dev o pro).
  • Recursos. Aquí definimos los servicios en la nube utilizados. Una función Lambda, un rol de ejecución de Lambda con los permisos adecuados, el desencadenador que conecta la función con el servicio de Alexa y por último la base de datos DynamoDB.
  • Parámetros de salida. La información que queremos devolver, en éste caso el ARN de la función Lambda, necesario para enlazar la skill de Alexa.

Con dicha plantilla, podemos acceder al servicio CloudFormation y crear un nuevo Stack, como podemos ver en siguiente imagen:

Formulario para completar los parámetros definidos

Una vez completados los datos, confirmamos la creación del stack y en cuestión de minutos CloudFormation se encarga de configurar todos los recursos. Al final del proceso, nos informará del ARN de la función Lambda creada y tendremos toda la infraestructura preparada.

¿Qué es y cómo funciona SAM (AWS Serverless Application Model)?

AWS Serverless Application Model, o SAM, es un framework de código abierto para facilitar el desarrollo de aplicaciones serverless. Simplifica la creación de plantillas, incluye un CLI e incluso un entorno de ejecución local de Lambda para poder ejecutar y depurar de forma local los servicios. SAM utiliza CloudFormation para gestionar todos los recursos en la nube y lo hace traduciendo las plantillas simples de SAM a CloudFormation.

Para empezar con el mismo ejemplo, definiremos una plantilla, muy similar a la de CloudFormation.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-alexa

  Sample SAM Template for ask node

Parameters:
  SkillName:
    Type: 'String'
    Description: 'Required. The Alexa Skill name'
    Default: 'helloworld'
  CodeLocation:
    Type: 'String'
    Description: 'Lambda code location'
    Default: './lambda/'
  Stage:
    Type: String
    Description: 'Environment for deploy'
    Default: dev
    AllowedValues:
      - dev
      - prod

Globals:
  Function:
    Runtime: nodejs8.10
    Timeout: 7
    Handler: index.handler
    CodeUri: !Ref CodeLocation

Resources:
  AlexaFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      FunctionName: !Sub 'ask-${Stage}-${SkillName}'
      Environment:
       Variables:
         TABLE_NAME: !Ref AttributesTable
      Events:
        AlexaSkillEvent:
          Type: AlexaSkill # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref AttributesTable
        #- Statement:
        #   Action:
        #     - dynamodb:Get*
        #     - dynamodb:PutItem
        #     - dynamodb:UpdateItem
        #   Effect: Allow
        #   Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${AttributesTable}

  AttributesTable:
   Type: AWS::Serverless::SimpleTable
   TableName: !Sub 'ask-${Stage}-${SkillName}'
   Properties:
     PrimaryKey:
       Name: id
       Type: String


Outputs:
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  AlexaFunction:
    Description: "Alexa Lambda Function ARN"
    Value: !GetAtt AlexaFunction.Arn

Para poder utilizar la plantilla, podemos utilizar el CLI. Después de copiar el fichero YAML a la carpeta del proyecto, donde tengamos el código de nuestra skill, ejecutamos los siguientes comandos:

sam deploy --guided

Posteriormente, nos va a solicitar los parámetros, que podemos apreciar en la siguiente imagen:

SAM Deploy example

Con estos comandos, SAM empaqueta el código, crea los recursos en la nube y los configura. Todo de manera automática, sin necesidad de acceder a la consola de AWS. Una de las cosas de las que SAM nos abstrae es de la necesidad de gestionar permisos, verifica de manera automática la creación de roles y políticas. La creación de un rol y política quedaría así:

DynamoDBCrudPolicy:
    TableName: !Ref AttributesTable

¿Cuál es mejor: Cloudformation o SAM?

La elección de una frente a otra va a depender de las necesidades del proyecto, siendo las dos válidas para gestionar de manera automática los recursos de un proyecto en infraestructura serverless.

Los principales puntos que debes valorar para tomar tu decisión serán:

Facilidad de uso y curva de aprendizaje

SAM es más fácil de utilizar, dado que abstrae ciertos componentes como la creación de roles y políticas de seguridad. Además, tiene un lenguaje más sencillo y cuenta con un CLI, que permite acelerar el desarrollo y gestionarlo todo desde una terminal, sin tener que acceder a la consola de administración de AWS.

Permisos y flexibilidad de configuración

La abstracción de la gestión de los permisos de seguridad que encontramos en SAM también puede ser una desventaja en ciertos entornos. En ciertas políticas de gobierno de la seguridad, no vamos a poder darle permisos de administración a una herramienta externa. En dicho caso, es necesario utilizar CloudFormation para evitar que desde un componente externo a AWS podamos crear recursos. Además, CloudFormation nos permite controlar y conocer la configuración en detalle de todos los servicios, al contrario que SAM que no permite conocer el detalle de los parámetros.