Reference lambda function Arn from another nested Stack - aws-lambda

I have two sam applications one 'App1' having a lambda function and another one 'App2' that will consume its Arn to create a permission like the following:
App2 template:
Parameters:
LambdaFunctionArnFromApp1:
Type: String
Description: The shared value will be passed to this parameter by parent stack.
DACAdminsLoginPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: LambdaFunctionArnFromApp1
Principal: apigateway.amazonaws.com
SourceArn: !Sub arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${HttpWebServer}/*/*/digitalatcore/admins/login
App1 template:
LambdaFunctionArnFromApp1:
Type: AWS::Serverless::Function
Properties:
CodeUri: 'URL'
Runtime: nodejs12.x
Handler: app.handler
Outputs:
LambdaFunctionArnFromApp1:
Value: !GetAtt LambdaFunctionFromApp1.Arn
Export:
Name: LambdaFunctionArnFromApp1
When i try to deploy the full stack with sam i get the following error :
"*** was not successfully created: The following resource(s) failed to create: [DACAdminsLoginPermission]. ROLLBACK_IN_PROGRESS"
Can anyone please help me with this.
thank you.

Assuming both nested stacks have the same parent stack, you can pass the output from the App1 stack to the App2 stack like this:
App1Stack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: app1-template.yaml
App2Stack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: app2-template.yaml
Parameters:
LambdaFunctionArnFromApp1: !GetAtt App1Stack.Outputs.LambdaFunctionArnFromApp1
Another option could be to use the ImportValue intrinsic function in your App2 template to get the export that you defined in the App1 template: FunctionName: !ImportValue LambdaFunctionArnFromApp1

Related

How to pass the Arn of a Step function to another function in a CloudFormation stack?

I created a step function in a CloudFormation stack, and I need to call that function from another function in the stack once it's deployed. I have no idea how to do that, and I'm getting circular dependencies.
Basically I'm trying to pass an environment variable to the function that's the ARN of the step function.
Here's the CloudFormation code :
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
reports stack
Globals:
Function:
Timeout: 120
MemorySize: 2048
Runtime: python3.9
Environment:
Variables:
STEP_FUNCTION: !GetAtt Research.Arn ### =====> how do i do that ?
Parameters:
ProjectName:
Description: Name of the Project
Type: String
Default: Project
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
TracingEnabled: true
Cors:
AllowMethods: "'DELETE,GET,HEAD,OPTIONS,POST,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
List:
DependsOn: VideoResearch
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/
Handler: get.get
Events:
List:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /reports
Method: GET
Policies:
(...)
################ STEP FUNCTION ################
Research:
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/
Handler: runResearch.research
Policies:
(...)
StepFunctionToHandleResearchRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- !Sub states.${AWS::Region}.amazonaws.com
Action: "sts:AssumeRole"
Path: "/"
Policies:
(...)
#
StepFunctionToHandleVideosResearch:
Type: "AWS::StepFunctions::StateMachine"
Properties:
DefinitionString: !Sub |
{
"StartAt": "Research",
"States": {
"Research": {
"Type": "Task",
"Resource": "${VideoResearch.Arn}",
"End": true
}
}
}
RoleArn: !GetAtt [ StepFunctionToHandleResearchRole, Arn ]
Outputs:
(...)
# I also tried to export the arn of the function
In my function's code I have :
stepFunctionARN = os.environ['STEP_FUNCTION']
#Anthony B. posted the answer in the comments : the problem was related to the fact I added the dependency as global variables, it created a circular dependency (API=> local function <= API looking for function's ARN => doesn't work).
If you remove the global variable and add a local variable, with a "DependsOn: Research" than everything works.
API=>created => creates first function, get Arn => than creates other functions and provide ARN

SAM give access to Cognito

I want to be able to call cognito functions through boto3 from my Lambda function in Python environment. What's the best way to give this type of access? I've done the following yaml but not sure if that's the best practice or I'm making the template longer.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
Resources:
HelloWorldFunction:
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:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- AWSLambdaExecute # Managed Policy
- Version: '2012-10-17' # Policy Document
Statement:
- Effect: Allow
Action:
- cognito-idp:ListUsers
Resource: 'arn:aws:cognito-idp:us-east-2:****:*****'
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: get
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# 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
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
I'm talking about the "Policies", is my template up to the standards? or is there a shortcut I can take?

Specify resources allowed to call a function in its AWS SAM Function template

TL; DR: How should I edit the template below so that it can be triggered by a user pool trigger?
I try to crate a CloudFormation template for a Lambda function defining both the services the function can call and be called from. It should be run with a Cognito User Pool trigger.
To do that, I've defined a resource in template of type AWS::Serverless::Function briefly as follows. Watch out the Policies section:
Resources:
MyFunctionResource:
Type: AWS::Serverless::Function
Properties:
FunctionName: MyFunctionName
CodeUri: ./
Handler: "lambda_function.lambda_handler"
MemorySize: 128
Runtime: python3.7
Timeout: 3
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "cognito-idp:*"
- "logs:*"
...
Resource: "*"
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "lambda:InvokeFunction"
Principal:
Service: cognito-idp.amazonaws.com
Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunctionName"
The second policy I have inserted for restricting the resources can call my function fails during the stack creation:
Policy document should not specify a principal. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
When I remove that policy with principal, the access to the function through the user pool trigger is denied.
I figured out that the permissions should be created as a separate resource with type AWS::Lambda::Permission which can take the function name or arn it will be attached to.
Thus, the following logic creates the function with permissions (a.k.a. Function Policy) successfully:
Resources:
MyFunctionResource:
Type: AWS::Serverless::Function
Properties:
FunctionName: MyFunctionName
CodeUri: ./
Handler: "lambda_function.lambda_handler"
MemorySize: 128
Runtime: python3.7
Timeout: 3
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "cognito-idp:*"
- "logs:*"
...
Resource: "*"
## Remove this section
# - Version: "2012-10-17"
# Statement:
# - Effect: Allow
# Action: "lambda:InvokeFunction"
# Principal:
# Service: cognito-idp.amazonaws.com
# Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunctionName"
## Add this instead
MyFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt MyFunctionResource.Arn
Principal: "cognito-idp.amazonaws.com"
SourceArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*"

Can i add a codecommit trigger to my lambda function via CloudFormation

Im writing a lambda function that i want to be triggered by somebody updating the master branch of a repo. The repo already exists on the account.
Is there a way in cloudformation that i can add the trigger to the lambda function? I guess at a snip i could cretae some cloudwatch rule to trigger the lambda, but would rather keep it all inside the lambda.
Thanks
R
If you are using AWS serverless transform then you can self contain it within the lambda. Although the transform generates the cloudwatch rule and the lambda permission, so it's basically the same you mentioned.
nevertheless here's an example to do what you want
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Pipeline which triggers lambda for codecommit changes
Parameters:
BranchName:
Default: master
Description: The GIT branch
Type: String
RepositoryName:
Description: The GIT repository
Type: String
StackOwner:
Description: The stack owner
Type: String
Resources:
BasicLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: logs:*
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action: '*'
Resource: '*'
PipelineTriggerFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/trigger
Handler: codetrigger.handler
MemorySize: 256
Role: !GetAtt BasicLambdaRole.Arn
Runtime: python3.6
Timeout: 900
Environment:
Variables:
TestVariable: "TestValue"
Events:
CodeCommitPushEvent:
Type: CloudWatchEvent
Properties:
Pattern:
source:
- aws.codecommit
resources:
- !Sub 'arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName}'
detail:
event:
- referenceCreated
- referenceUpdated
repositoryName:
- !Ref RepositoryName
referenceName:
- !Ref BranchName
Tags:
'owner': !Ref StackOwner
'task': !Ref RepositoryName
Obviously, specify the lambda role better and not give all permissions as provided in the example.

Setting Access Role for Event Stream Created Via CloudFormation

I'm trying to add a dynamodb stream with the following template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Role:
Fn::ImportValue:
!Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
However, I'm getting the following error during the deploy stage:
Please ensure the role can perform the GetRecords, GetShardIterator, DescribeStream, and ListStreams Actions on your stream in IAM.
Attempt 1
So I tried fixing the problem by adding the following policies to my IAM, CodeStarWorker-myproject-CloudFormation:
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams",
That didn't work, still giving me the same error
Attempt 2
Tried using policies stead of role in template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Policies:
- IAMFullAccess
- AWSLambdaFullAccess
But it gave me the following error
API: iam:CreateRole User: arn:aws:sts::xxx:assumed-role/CodeStarWorker-xxx-CloudFormation/AWSCloudFormation is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::xxx:role/awscodestar-xxx-lambda-MyFuncRole-1BO7G545IR5IC
Attempt 3
Specifying a role in template.yml
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow #allow lambda to assume this role
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: LambdaRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # allow to write logs to cloudwatch
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow # allow lambda to read from the event stream
Action:
- dynamodb:DescribeStream
- dynamodb:GetRecords
- dynamodb:GetShardIterator
- dynamodb:ListStreams
Resource: "*"
And assign it to MyFunc
Role:
Fn::GetAtt: [ LambdaRole , Arn ]
However, it's also giving me the same error indicating that I'm not authorized to perform iam:CreateRole
Any help?
iam:CreateRole - you would need this action to create a role. The user that you use to run the Cloudformation template would need to include the "CreateRole" action.

Resources