I may just be having trouble, but it would be great to have "DependsOn" working with serverless for the more complicated deploys.
For instance, if I wanted to create a LambdaRole and Lambda I could say
Transform: AWS::Serverless-2016-10-31
Resources:
Lambda:
DependsOn: LambdaRole
Type: AWS::Serverless::Function
Role: !Ref LambdaRole
LambdaRole:
Type: AWS::IAM::Role
Right now I'm getting a "Circular Dependency" issue. I'm sure it's supported but I wanted to check with you to make sure I'm not doing anything wrong.
Agreed. DependsOn is a good feature to have. But I don't see how this template would create a circular dependency issue. Can you provide the full template?
I take it that AWS::Serverless::Function does not support DependsOn At this time? In my CloudFormation stack, I have a section like
LambdaRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
LambdaRolePolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "LambdaRolePolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource:
- "*"
Roles:
-
Ref: "LambdaRole"
Lambda:
Type: "AWS::Serverless::Function"
Properties:
Role:
!GetAtt LambdaRole.Arn
Handler: lambda_function.lambda_handler
Runtime: python2.7
Timeout: 15
CodeUri: "s3://..."
DependsOn: LambdaRolePolicy
When I run my CloudFormation stack, the Lambda starts creating immediately after the LambdaRole finishes creation, and before the LambdaRolePolicy event starts it's creation, thus, I get errors like
Error Message: User: arn:aws:sts::***:assumed-role/LambdaRole-***/Lambda-*** is not authorized to perform: logs:CreateLogGroup on resource: arn:aws:logs:us-west-2:***:log-group:/aws/lambda/Lambda-***:log-stream:
Is there a work around to make the lambda function wait until the policy has been attached to the role?
Alternatively, you could make use of Managed Policy and not have an extra resource in here:
LambdaRole:
Type: "AWS::IAM::Role"
Properties:
# Managed Policy include required Lambda Execution Role permissions
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
Lambda:
Type: "AWS::Serverless::Function"
Properties:
Role: !GetAtt LambdaRole.Arn
Handler: lambda_function.lambda_handler
Runtime: python2.7
Timeout: 15
CodeUri: "s3://..."
Removed DependsOn in Serverless::Function as it's not supported and merged Policy and Role by making use of AWSLambdaBasicExecutionRole that does exactly the same that IAM policy above.
That should allow Lambda to assume just fine and add additional policies right after if you need to. Give it a try and let us know here :)
Hope it helps
Thanks @heitorlessa, I also discovered an alternative method.
The following is also equivalent to what I put in originally:
Lambda:
Type: "AWS::Serverless::Function"
Properties:
Handler: lambda_function.lambda_handler
Runtime: python2.7
Timeout: 15
CodeUri: "s3://..."
This works because the AWS::Serverless::Function actually generates a number of CloudFormation resources. In the case above, it will generate a role for the function, because one is not explicitly assigned, and it will attach the AWSLambdaBasicExecutionRole to it.
Further, for anyone else facing a similar issue, if you need to attach custom permissions, you can do so using:
Lambda:
Type: "AWS::Serverless::Function"
Properties:
Handler: lambda_function.lambda_handler
Runtime: python2.7
Timeout: 15
CodeUri: "s3://..."
Policies:
-
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "s3:GetObject"
Resource:
- "arn:aws:s3:::<some_bucket>/*"
This will create a custom role, with the AWSLambdaBasicExecutionRole Policy attached, and also attach inline policies. In the case above, this will create an inline policy that grants the lambda access to get objects from <some_bucket>.
Internally, it appears that AWS::Serverless::Function seems to use AWS::IAM::Role to generate the role for the function, and passes in your defined list of Policies to the roles Policies property, and waits for it to complete before creating the lambda function that uses it.
What would be a good way of attaching multiple managed policies to this?
@jrstarke that's right, AWS::Serverless::Function does create implicit resources including an IAM Role (and Lambda Permissions in case you define an API). Best way to capture these resources as documentation gets updated is to simply describe resources that Cloudformation created.
My suggestion on having an IAM role within the template is that it allows you the flexibility to add Managed Policy (multiples) and Inline policies at the same time since it's a cloudformation resource and not a SAM translation, of which also helps in maintainability for those reading and using that SAM template - In fairness, it's a judgement call as both get the job done :)
@ktruckenmiller Following the example above you can add multiple managed policies as follows - Simply include the full ARN whether that be customer or AWS managed policy:
ServiceXLambdaRole:
Type: "AWS::IAM::Role"
Properties:
# Managed Policy include required Lambda Execution Role permissions
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
- "arn---policy2"
- "arn---policy3"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
AWS::Serverless::Function can take multiple Managed Policy names. Like this:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs4.3
Policies:
- AWSS3FullAccess
- AWSDynamoDBFullAccess
SAM will automatically add LambdaBasicExecution role to the permissions.
Above will work for AWS Managed Policies. If you are attaching your own Custom Managed policy, you need to follow @heitorlessa's example above. Create your own IAM role and attach the role to SAM function
To get the conversation back on track: without DependsOn support there is no real way to build bootstrapping actions in SAM. This needs to be implemented and should be marked as a bug not a question.
Yup. Supporting DependsOn is a feature request. We will get it prioritized
Hi @jrstarke, You may also create a cloudformation stack with the Role and export it to use it in another Cloudformation stack where you will create your Lambda Function
First cloudformation stack
Outputs:
FunctionRole:
Description: Role to be used.
Value:
!GetAtt LambdaRole.Arn
Export:
Name: !Sub "${AWS::StackName}-Role"
In the second cloudformation stack
Parameters:
LambdaRoleStackName:
Description: Name of an active CloudFormation stack that contains the Role, that will be used in this stack.
Type: String
MinLength: 1
MaxLength: 255
AllowedPattern: ^[a-zA-Z][-a-zA-Z0-9]*$
Default: SampleCrossStack
Resources:
LambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Role:
Fn::ImportValue: !Sub "${LambdaRoleStackName}-Role"
However, I prefer your workaround ;)
+1 for DependsOn support.
Also, some documentation on the implicit resources created would be helpful (I just got tripped by a MyLambdaMyLambdaSchedulePermission generated by a MyLambda Serverless::Function with MyLambdaSchedule as an Event)
Not sure if it deserves a separate issue, but Conditional creation of Function should be supported as well.
SAM resources should now supports DependsOn. Can you check?
Condition is coming soon as well.
I didn't get an error using the DependsOn attribute, but it didn't seem to be effective.
@ShaunEgan What do you mean? Did the DependsOn property get preserved in the transform?
To other people who ends up here, "DependsOn" works, just put it after "Type".
DependsOn is available to use. Seems we have missing documentation for it though.
Updated labels to reflect the current state of this. Will close issue once docs are updated.
@sanathkr @jfuss I tried DependsOn but could not get it to work. Are you sure DependsOn is also copied to the LambdaFunction Role resource?
When the role creation depends on creation of another resource, that resource is not created before the lambda function role is created.
So the DependsOn is not propagated to the Role resource. Do you want to create some resource first, followed by the IAM Role, then the Lambda function?
If the IAM role contains !Ref or !GetAtt on the resource, then CloudFormation will implicitly add a dependency between them. Is this not the case?
To create the generated Role, the cloudformation role needs to have the iam:CreateRole policy.
I am adding that policy to the cloudformation role in the template, just before the serverless function.
Without the dependency, the Lambda role is created before cloudformation role has the policy to do that.
Thanks for the hint aboud !Ref and !GetAtt. There may be a way to create the !Ref in the Lambda Policy definition without actually using it.
By "CloudFormation role", do you mean the role you use to deploy the stack with? More precisely, the IAM user calling the CloudFormation deploy command would have this role. If this is the case, you should move this role outside of the stack. Otherwise it is impossible to create a least-privilege IAM user that can update your app stack.
Best practice is to create a "infrastructure stack" that creates the IAM Roles/users necessary to manage your "application stack". App stacks contain Lambda function/api gateway etc. With Stack Policies, you can allow only certain users to manage your infrastructure stack. This is mostly administrators. Application developers can be given full access to manage app stacks, without needing admin permissions.
Yes. In our situation the deploy command is and shall only be excuted by CodePipeline. In understand your point, but the content of the stack template is tightly coupled with the required permissions of it's deployment role. things become quite unmanageable if they are dislocated. To restrict persimssons you do want a separate role for each stack I presume?
I am trying to find a way to implement your proposed 'best practice' in a scalable way.
Scernario: an application developer adds a lambda function to an application template.
He needs to add a whole range of lambda persissions to the ExecuteChangeSet service role used by the codepipeline and it needs to be automated.
I guess you suggest there'd be another stack managing that role. However, the template for that stack would be in yet another repo the needs to be redeployed before the application stack can be deployed?
Most helpful comment
Yup. Supporting DependsOn is a feature request. We will get it prioritized