I have this cloudformation template and it works as expected. It will add 1 record to elasticsearch index.
But I am not sure if the Role is defined correctly. I need to set the minimum permissions for this function.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Gateway and Lambda function for mailgun API"
Resources:
lambdaIAMRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/furl:*"
PolicyName: "lambda"
lambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
def handler(event,context):
from elasticsearch import Elasticsearch
es = Elasticsearch(
['search-test-XXXX.us-east-1.es.amazonaws.com'],
http_auth=('root', 'XXXX)'),
scheme="https",
port=443,
)
doc = {
'author': 'kimchy',
'text': 'Elasticsearch: cool. bonsai cool.'
}
res = es.index(index="test-index", id=1, body=doc)
Description: "EmailThis"
FunctionName: elast
Handler: "index.handler"
MemorySize: 128
Role: !GetAtt "lambdaIAMRole.Arn"
Runtime: "python3.6"
Timeout: 100
Layers:
- "arn:aws:lambda:us-east-1:770693421928:layer:Klayers-python38-elasticsearch:12"
Yes, the permissions in the role match those from standard AWSLambdaBasicExecutionRole.
However, your role will not work, as it has incorrect resource. Instead of:
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/furl:*"
there should be:
Resource:
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/elast:*"
since elast is the name of your function, and not furl.
Related
I want CloudWatch to send CreateLogGroup messages to EventBridge.
I understand this is possible, but also that Cloudwatch doesn't seem to send these messages by default. It seems you have to configure CloudTrail to get it to forward the message. But I can't find a CloudTrail configuration which works - in general deployment fails with:
AWS::CloudTrail::Trail - "Invalid request provided: Incorrect S3 bucket policy is detected for bucket"
AWSTemplateFormatVersion: '2010-09-09'
Outputs:
HelloFunction:
Value:
Ref: HelloFunction
WatcherFunction:
Value:
Ref: WatcherFunction
WatcherTrailBucket:
Value:
Ref: WatcherTrailBucket
Parameters:
MemorySizeDefault:
Default: '512'
Type: String
RuntimeVersion:
Default: '3.8'
Type: String
TimeoutDefault:
Default: '5'
Type: String
Resources:
HelloFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
print (event)
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- HelloFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
HelloFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: hello-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
WatcherFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
print (event)
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- WatcherFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
WatcherFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: watcher-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
WatcherEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.logs
detail-type:
- "AWS API Call via CloudTrail"
detail:
eventName:
- CreateLogGroup
Targets:
- Id:
Fn::Sub: watcher-event-rule-${AWS::StackName}
Arn:
Fn::GetAtt:
- WatcherFunction
- Arn
State: ENABLED
WatcherEventRulePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
FunctionName:
Ref: WatcherFunction
SourceArn:
Fn::GetAtt:
- WatcherEventRule
- Arn
WatcherTrailBucket:
Type: AWS::S3::Bucket
WatcherTrailBucketPolicy:
DependsOn:
- WatcherTrailBucket
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: WatcherTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: "*"
Condition: {}
- Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: "*"
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
WatcherTrail:
Type: AWS::CloudTrail::Trail
Properties:
EventSelectors:
- ReadWriteType: All
IsLogging: true
S3BucketName:
Ref: WatcherTrailBucket
IsLogging: true
S3KeyPrefix: logs/
Your WatcherTrail runs before WatcherTrailBucketPolicy, so that's why it fails (CloudFormation does not deploy resource in the order of their definition in the template). Add explicit DependsOn dependency on the bucket policy. Also your WatcherTrailBucketPolicy is incorrect, and trial needs a name. So it should be:
WatcherTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: WatcherTrailBucket
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck20150319",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::${WatcherTrailBucket}",
"Condition": {
"StringEquals": {
"aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
}
}
},
{
"Sid": "AWSCloudTrailWrite20150319",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::${WatcherTrailBucket}/logs/AWSLogs/${AWS::AccountId}/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
}
}
}
]
}
WatcherTrail:
Type: AWS::CloudTrail::Trail
DependsOn: WatcherTrailBucketPolicy
Properties:
TrailName: MyTrial
EventSelectors:
- ReadWriteType: All
IsLogging: true
S3BucketName:
Ref: WatcherTrailBucket
IsLogging: true
S3KeyPrefix: logs/
Considering this lambda function on a serverless.yml file:
functions:
s3toEc2Lambda:
handler: s3toec2lambda.S3toEc2Lambda
name: "${self:service}-s3toEc2Lambda"
role: S3toEc2LambdaRole
And considering this SNS created on resources section: Does someone knows how to inform the Sns ARN Endpoint from the lambda function s3toEc2Lambda ?
resources:
Resources:
WordpressFrontEndSnsS3toEc2:
Type: AWS::SNS::Topic
Properties:
TopicName: "wordpress-front-end-s3-ec2"
WordpressFrontEndSnsS3toEc2Lambda:
Type: AWS::SNS::Subscription
Properties:
Endpoint: { "Fn::GetAtt": ["s3toEc2Lambda", "Arn" ] } <------ HERE <------
#Endpoint: ${self:functions.s3toEc2Lambda} <------ OR HERE <------
#Endpoint: { "Fn::GetAtt": ["${self:functions.s3toEc2Lambda}", "Arn" ] } <------ OR HERE <------
Protocol: lambda
TopicArn: !Ref 'WordpressFrontEndSnsS3toEc2'
For me always appear a error message like this: "Template error: instance of Fn::GetAtt references undefined resource s3toEc2Lambda"
Thank You !
CloudFormation resources created by serverless have known format. For lambda function this is:
{normalizedFunctionName}LambdaFunction
Thus you should be able to reference your function using the following:
"Fn::GetAtt": [ S3toEc2LambdaLambdaFunction, Arn ]
More example about this are here
We can create Function Roles, Functions Policy and Lambda functions SAM template.yml file by this
Type: AWS::IAM::Role
Properties:
RoleName: UatAdminUserStatsLambda
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service:
- 'lambda.amazonaws.com'
Action:
- 'sts:AssumeRole'
FunctionPolicy:
Type: AWS::IAM::Policy
DependsOn: FunctionRole
Properties:
PolicyName: UserStatsPolicy
Roles:
- !Ref FunctionRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'iam:GetUser'
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:GetLogEvents'
- 'logs:PutLogEvents'
- 's3:GetObject'
- 's3:PutObject'
Resource: '*'
adminUsersList:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/admin/
Role: !GetAtt FunctionRole.Arn
Handler: adminUsersList.adminUsersList
Layers:
- !Ref NodeDependenciesLayer
Events:
adminUsersListEvent:
Type: Api
Properties:
Path: /api/admins
Method: GET
I've two stacks called "createIAMRole", "createElasticSearch" and "createdLambda". What I want is I want to use IAM Role ARN from first stack called "createIAMRole" in both "createElasticSearch" and "createdLambda".
createIAMRole
AWSTemplateFormatVersion: '2010-09-09'
Description: >
blah.
Resources:
myIAMRole:
Type: AWS::IAM::Role
Properties:
..
..
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:*
Resource: "*"
PolicyName: "myIAMRolePolicy"
Outputs:
myIAMRole:
Description: myIAMRole to use Stacks
Value: !Ref myIAMRole
"createElasticSearch"
Resources:
ElasticsearchDomain:
Type: AWS::Elasticsearch::Domain
Properties:
..
..
AccessPolicies:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS:
- >>> THERE IS I WANT TO ADD <<<
Action: "es:*"
Resource: "*"
AdvancedOptions:
rest.action.multi.allow_explicit_index: "true"
Please let me know how to do it, thanks.
For the createIAMRole you need to export the output:
Outputs: myIAMRole:
Description: myIAMRole to use Stacks
Value: !Ref myIAMRole
Export:
Name: myIAMRole
And for the createElasticSearch you need to "ImportValue":
Fn::ImportValue:
!Sub "${myIAMStackName}-myIAMRole"
More Information: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
I currently have the following cloudformation .yaml file:
Resources:
DynamoTable:
Type: "AWS::DynamoDB::Table"
Properties:
...
...
...
How do I give other resources permission to query this table?
Resources:
Service:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
...
...
TaskPolicyArn: !Ref ThisServicePolicy
DynamoTable:
Type: "AWS::DynamoDB::Table"
Properties:
AttributeDefinitions:
...
...
...
ThisServicePolicy:
Type: "AWS::IAM::ManagedPolicy"
Properties:
ManagedPolicyName: SomePolicyName
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "dynamodb:GetItem"
- "dynamodb:BatchGetItem"
- "dynamodb:Query"
Resource: "*"
I have made a nested cloudformation stack that in this case references a Lambda child stack.
Because I have multiple LambdaFunctions, I designed the LambdaFunction resource in the Lambda child
template such that it can repeat the same actions across all the Lambda Functions specified in the Parent template.
However, I get the following error once I execute create-stack: Template error: every Fn::GetAtt object requires two non-empty parameters, the resource name and the resource attribute, which is pointing to the Lambda Child template.
I tried adding a
DependsOn clause in which I listed all the LambdaExecutionRoles, since the LambdaFunction references those, but that
didn't appear to resolve the issue. So something is either going wrong with taking in the LambdaName parameter
or grabbing the Arn. Any thoughts?
Portion of Parent template:
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
AlignmentLambdaFuncS3BucketName:
Type: String
AlignmentLambdaFuncS3KeyName:
Type: String
AlignmentLambdaFuncModuleName:
Type: String
HaploLambdaFuncS3BucketName:
Type: String
HaploLambdaFuncS3KeyName:
Type: String
HaploLambdaFuncModuleName:
Type: String
Resources:
AlignmentLambdaFunction:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Alignment
BucketName: LambdaFuncS3BucketName
S3KeyName: LambdaFuncS3KeyName
ModuleName: LambdaFuncModuleName
TemplateURL: https://s3.amazonaws.com/CFNTemplate/lambda_resources.stack.yaml
TimeoutInMinutes: 1
HaploLambdaFunction:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Haplo
BucketName: LambdaFuncS3BucketName
S3KeyName: LambdaFuncS3KeyName
ModuleName: LambdaFuncModuleName
TemplateURL: https://s3.amazonaws.com/CFNTemplate/lambda_resources.stack.yaml
TimeoutInMinutes: 1
Portion of Lambda child template:
AWSTemplateFormatVersion: '2010-09-09'
Description: lambda function and execution role stack.
Parameters:
LambdaName:
Type: String
BucketName:
Type: String
S3KeyName:
Type: String
ModuleName:
Type: String
KMSAdminUserARN:
Type: String
KMSEndUserARN:
Type: String
Resources:
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Handler: !Sub '${LambdaName}-{ModuleName}.handler'
Role:
Fn::GetAtt: [ !Sub '${LambdaName}LambdaExecutionRole', Arn ]
Code:
S3Bucket: !Sub '${LambdaName}{BucketName}'
S3Key: !Sub '${LambdaName}{S3KeyName}'
Runtime: "python3.6"
AlignmentLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: CanListBuckets
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetBucketLocation"
- "s3:ListAllMyBuckets"
Resource: "arn:aws:s3:::*"
- PolicyName: CanCallBatch
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "batch:*"
Resource: "*"
- PolicyName: CanLog
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
HaploLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: CanListBuckets
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetBucketLocation"
- "s3:ListAllMyBuckets"
Resource: "arn:aws:s3:::*"
- PolicyName: CanCallBatch
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "batch:*"
Resource: "*"
- PolicyName: CanLog
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
Unfortunately, you can't use any functions (for example, Sub) inside Fn::GetAtt's logical resource name:
For the Fn::GetAtt logical resource name, you cannot use functions. You must specify a string that is a resource's logical ID.
Source: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
+1 to divinedragon's solution. You should be able to abstract away the *ExecutionRole resources into a separate file, have that file output each resources arn, and then pick that up directly in the lambda template. Some pseudo code that has worked for me in the past (I use exports here, but you can just pass by parameter if you can't):
ExecutionRoleTempate.yml:
Resources:
ExecutionRole1:
Type: "AWS::IAM::Role"
...
ExecutionRole2:
Type: "AWS::IAM::Role"
...
Output:
ERArn1:
Value: Fn::GetAtt ExecutionRole1.arn
Export:
Name: ERArn1
ERArn2:
Value: Fn::GetAtt ExecutionRole2.arn
Export:
Name: ERArn2
lambda_resources.stack.yaml:
Resources:
LambdaFunction:
...
Role: Fn::ImportValue !Sub ....