amazon-archives / serverless-app-examples

Apache License 2.0
717 stars 402 forks source link

Missing example for how to configure/enhance the implicitly generated CodeDeployServiceRole #21

Closed harryherbig closed 4 years ago

harryherbig commented 4 years ago

While evaluating aws sam as a CD solution for lambda functions I had struggles finding appropriate guides or documentation, and had to combine random examples and guides to get my "safe deployment" pipeline working.

I managed to setup everything after consulting different github repos and guides, but the CodeDeploy Deployment fails with this message:

The IAM role arn:aws:iam::${awsacc}:role/${lambda-app-name}-CodeDeployServiceRole-${generated-version} does not give you permission to perform operations in the following AWS service: AWSLambda. Contact your AWS administrator if you need help. If you are an AWS administrator, you can grant permissions to your users or groups by creating IAM policies.

The implicitly generated IAM Role looks like this:

{
    "Role": {
        "Path": "/",
        "RoleName": "${lambda-app-name}-CodeDeployServiceRole-${generated-version}",
        "RoleId": "${SOME_ROLE_ID}",
        "Arn": "arn:aws:iam::${awsacc}:role/${lambda-app-name}-CodeDeployServiceRole-${generated-version}",
        "CreateDate": "2020-09-15T16:29:41+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "codedeploy.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        "Description": "",
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {
            "LastUsedDate": "2020-09-15T16:44:09+00:00",
            "Region": "eu-west-1"
        }
    }
}

this is my template.yaml:

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

Globals:
  Function:
    Timeout: 20

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sam-poc-helloworld
      AutoPublishAlias: live
      DeploymentPreference:
        Type: AllAtOnce
        Hooks:
          PreTraffic: !Ref PreLiveHook
        Alarms:
          - !Ref AliasErrorMetricGreaterThanZeroAlarm
          - !Ref LatestVersionErrorMetricGreaterThanZeroAlarm
      CodeUri: HelloWorldFunction
      Handler: helloworld.App::handleRequest
      Runtime: java11
      MemorySize: 512

  DeploymentBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: sam-poc-deployment-artifacts

  PreLiveHook:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sam-poc-prelivehook
      CodeUri: PreLiveHook
      Handler: prelive.Validator::handleRequest
      Runtime: java11
      MemorySize: 512
      Environment:
        Variables:
          NEW_VERSION: !Ref HelloWorldFunction.Version
      Policies:
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - codedeploy:PutLifecycleEventHookExecutionStatus
              Resource: !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - lambda:InvokeFunction
              Resource: !Sub
                - ${FunctionArn}:*
                - FunctionArn: !GetAtt HelloWorldFunction.Arn

  AliasErrorMetricGreaterThanZeroAlarm:
    Type: "AWS::CloudWatch::Alarm"
    Properties:
      AlarmDescription: Lambda Function Error > 0
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: Resource
          Value: !Sub "${HelloWorldFunction}:live"
        - Name: FunctionName
          Value: !Ref HelloWorldFunction
      EvaluationPeriods: 2
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 0

  LatestVersionErrorMetricGreaterThanZeroAlarm:
    Type: "AWS::CloudWatch::Alarm"
    Properties:
      AlarmDescription: Lambda Function Error > 0
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: Resource
          Value: !Sub "${HelloWorldFunction}:live"
        - Name: FunctionName
          Value: !Ref HelloWorldFunction
        - Name: ExecutedVersion
          Value: !GetAtt HelloWorldFunction.Version
      EvaluationPeriods: 2
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 0

Outputs:
  HelloWorldFunctionArn:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
harryherbig commented 4 years ago

Okay may have found the problem:

The FunctionName attribute explicitly tells CloudFormation what to name the function. Otherwise, CloudFormation creates the function with the default naming convention: [stackName]-[FunctionName]-[uniqueID]. Name the function with the “CodeDeployHook_” prefix because the CodeDeployServiceRole role only allows InvokeFunction on functions named with that prefix.

mentioned here: https://aws.amazon.com/blogs/compute/implementing-safe-aws-lambda-deployments-with-aws-codedeploy/

Yep, that solved the issue, so I close it.

A complete example which tackles all the obstacles in one repo would be helpful. I still don't know what kind of Event the pre-traffic-hook lambda will get as an Input, so I am using String for now, and the examples I found are using untyped javascript.