I'm writing a lambda to update an RDS instance. Apparently the RDS instance must be in a VPC, and the lambda must be in the VPC so it can access the RDS. Alright then. So I configure the lambda to be in the VPC, and then it doesn't have permissions to open network interfaces on the EC2, so I have to give the lambda a role which has those permissions.
Here's my serverless.yml:
service: users-to-rds
plugins:
- serverless-plugin-typescript
provider:
name: aws
runtime: nodejs6.10
stage: dev
timeout: 30
region: ap-southeast-2
iamRoleStatements:
- Effect: Allow
Action:
- "S3:*"
Resource: arn:aws:s3:::*
- Effect: Allow
Action:
- "rds:*"
Resource: "*"
vpc:
securityGroupIds:
- sg-stuff
subnetIds:
- subnet-stuff
- subnet-stuff
- subnet-stuff
functions:
writeToDB:
handler: write-users-to-rds.writeToDB
events:
- sns: userlists
role:
Fn::GetAtt: [ "doStuffFromInsideVPC", "Arn" ]
Resources:
SNSInvokeLambdaPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
Principal: "sns.amazonaws.com"
SourceArn:
Ref: UserListNotification
FunctionName:
Fn::GetAtt: [ "UserListToRDS", "Arn" ]
doStuffFromInsideVPC:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: '2017'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: doStuffFromInsideVPCPolicy
PolicyDocument:
Version: '2017'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
- - 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- ec2:DeleteNetworkInterface
Resource: "*"
When I do serverless deploy it makes the zip which gets uploaded to AWS and then I am told:
The CloudFormation template is invalid: Template error: instance of Fn::GetAtt references undefined resource doStuffFromInsideVPC
which is of course complete nonsense. There it is right there. Apparently there used to be a bug where CloudFormation hadn't created the role resource in time, so serverless was updated to add a DependsOn which causes it to wait until the role exists. And looking at .serverless/cloudformation-template-update-stack.json it seems to have a DependsOn where it needs it.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccelerateConfiguration": {
"AccelerationStatus": "Suspended"
}
}
},
"WriteToDBLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/users-to-rds-dev-writeToDB"
}
},
"WriteToDBLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/users-to-rds/dev/1515307801502-2018-01-07T06:50:01.502Z/users-to-rds.zip"
},
"FunctionName": "users-to-rds-dev-writeToDB",
"Handler": "write-users-to-rds.writeToDB",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"doStuffFromInsideVPC",
"Arn"
]
},
"Runtime": "nodejs6.10",
"Timeout": 30,
"VpcConfig": {
"SecurityGroupIds": [
"sg-stuff"
],
"SubnetIds": [
"subnet-stuff",
"subnet-stuff",
"subnet-stuff"
]
}
},
"DependsOn": [
"WriteToDBLogGroup",
"doStuffFromInsideVPC"
]
},
"WriteToDBLambdaVersionAOwtYsOPCw4hVAkxPXSzE66bZXvdbu2xQUzGAe58E": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "WriteToDBLambdaFunction"
},
"CodeSha256": "7gOzejhkEn37BviNCIVy1lw32IwPs9/oyeaPBQjlqVA="
}
},
"SNSTopicUserlists": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "userlists",
"DisplayName": "",
"Subscription": [
{
"Endpoint": {
"Fn::GetAtt": [
"WriteToDBLambdaFunction",
"Arn"
]
},
"Protocol": "lambda"
}
]
}
},
"WriteToDBLambdaPermissionUserlistsSNS": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"WriteToDBLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "sns.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:sns:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
"userlists"
]
]
}
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
}
},
"WriteToDBLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "WriteToDBLambdaVersionAOwtYsOPCw4hVAkxPXSzE66bZXvdbu2xQUzGAe58E"
}
}
}
}
Any ideas, anyone? Thank you.