Role ARN race condition with Lambdas in Resources section

I have a use case where my serverless application requires setting up of DNS and SSL certificates which I’m setting up automagically with https://github.com/ImmobilienScout24/aws-cf-verified-ssl-certificate.

I’ve done this successfully with vanilla CloudFormation, but when I try to set this up in Serverless, with the one-time-run Lambdas defined in the Resources section of the template, I get error “Value of property Role must be of type String.”

I’ve found that this error is often caused when a referenced resource is not ready, so I’ve used DependsOn to mitigate it. DependsOn doesn’t appear to be supported in AWS::Lambda::Function.

Is there a way to define these lambdas and roles so that the roles are ready before the Lambda attempts to use the ARN? I would prefer not to muddy my Functions section in serverless.yml with functions that are not part of my service.

SesDomainCustomResourceFunction:
  Type: AWS::Lambda::Function
  DependsOn: SesDomainCustomResourceRole
  Properties:
    Code:
      S3Bucket:
        Ref: ServerlessDeploymentBucket
      S3Key: ${self:custom.sslLambdaFunctions}
    Runtime: python2.7
    Handler: ses_domain_identity.lambda_handler
    Role:
      GetAtt: [ SesDomainCustomResourceRole, Arn ]
    Timeout: 30

SesDomainCustomResourceRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: SesDomainCustomResourceRole
    Policies:
      -
        PolicyName: SES
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            -
              Action:
                - ses:VerifyDomainIdentity
                - ses:DeleteIdentity
              Resource:
                - "*"
              Effect: Allow
      -
        PolicyName: cloudwatchLogsWriteAccess
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            -
              Action:
                - logs:Describe*
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource: "*"
              Effect: Allow
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        -
          Action: sts:AssumeRole
          Principal:
            Service: lambda.amazonaws.com
          Effect: Allow

Thanks!

1 Like

I think your issue is because while serveless.yml is (obviously) YAML, under the hood it’s converted in to JSON (because CloudFormation only started supporting YAML relatively recently). This means that while you’re writing YAML, you can’t use the YAML shorthand for CFN intrinsic functions.

I think what you need to do is update the Role property of your function to look more like this:

SesDomainCustomResourceFunction:
  Type: AWS::Lambda::Function
  DependsOn: SesDomainCustomResourceRole
  Properties:
    Code:
      S3Bucket:
        Ref: ServerlessDeploymentBucket
      S3Key: ${self:custom.sslLambdaFunctions}
    Runtime: python2.7
    Handler: ses_domain_identity.lambda_handler
    Role:
      Fn::GetAtt: [ SesDomainCustomResourceRole, Arn ]
    Timeout: 30

This uses the “old” function syntax (which you still have to use sometimes in YAML e.g. chaining functions).

1 Like

Sorry for the late reply. I solved my problem with the code below and went on vacation. Similar solution, though yours is easier on the eyes. Thank you!

   SesDomainCustomResourceFunction:
     Type: AWS::Lambda::Function
      DependsOn: SesDomainCustomResourceRole
      Properties:
        FunctionName: SesDomainCustomResourceFunction
        Code:
          S3Bucket: ${self:custom.sslLambdaFunctionsBucket}
          S3Key: ${self:custom.sslLambdaFunctions}
        Runtime: python2.7
        Handler: ses_domain_identity.lambda_handler
        Role:
          Fn::Join:
            - ":"
            - - arn
              - aws
              - iam
              - ''
              - Ref: AWS::AccountId
              - 'role/SesDomainCustomResourceRole'
        Timeout: 30
1 Like