yq replace a value with another value of a map - yaml

myFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myFunction
Handler: myFunction.lambda_handler
myOtherFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myOtherFunction
Handler: myOtherFunction.lambda_handler
I want to run a yq command such that for every Type:AWS::Serverless::Function resources, I'd like to grab the value of the Handler and make another attribute under properties called Environment.Variables.HANDLER.
I have following command so far.
yq '(.Resources.[] | select(.Type=="AWS::Serverless::Function") | .Properties.Environment.Variables.HANDLER) += (.Resources.[].Properties.Handler)' test.yaml
Which ends up with
myFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myFunction
Handler: myFunction.lambda_handler
Environment:
Variables:
HANDLER: myOtherFunction.lambda_handler # This is wrong
myOtherFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myOtherFunction
Handler: myOtherFunction.lambda_handler
Environment:
Variables:
HANDLER: myOtherFunction.lambda_handler
Where Environment.Variables.HANDLER is replaced with myOtherFunction's Handler for all the functions. How do I respectively grab the value from the particular resource to be replaced?

Use the update operator |= whenever you want to stay in context.
.Resources[]
|= select(.Type=="AWS::Serverless::Function").Properties
|= .Environment.Variables.HANDLER = .Handler
Or use the with function:
with(
.Resources[] | select(.Type=="AWS::Serverless::Function").Properties;
.Environment.Variables.HANDLER = .Handler
)
Both evaluate to:
Resources:
myFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myFunction
Handler: myFunction.lambda_handler
Environment:
Variables:
HANDLER: myFunction.lambda_handler
myOtherFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: myOtherFunction
Handler: myOtherFunction.lambda_handler
Environment:
Variables:
HANDLER: myOtherFunction.lambda_handler
Note: In your approach, using .Resources.[] a second time started iterating all over again, so all matching items received the last value fetched.

Related

In CloudFormation, how do I target a Lambda alias in Events::Rule

I'm trying to trigger a Lambda:alias (the alias is key here) on a schedule. The following code errors out with
"SampleLambdaLiveAlias is not valid. Reason: Provided Arn is not in
correct format. (Service: AmazonCloudWatchEvents; Status Code: 400;
Error Code: ValidationException;"
How do I properly target the lambda:alias in CloudFormation? I've tried !Ref, !Sub and just the logical name.
My custom-resource approach to retrieving the latest lambda version appears to be a necessary evil of setting up the "live" alias because AWS maintains old lambda versions, even after you delete the lambda and stack AND a valid version is required for a new alias. If anyone knows a more elegant approach to that problem, please see: how-to-use-sam-deploy-to-get-a-lambda-with-autopublishalias-and-additional-alises
SampleLambdaFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: SampleLambda
AutoPublishAlias: staging
CodeUri: src/
Handler: SampleLambda.handler
MemorySize: 512
Runtime: nodejs12.x
Role: !GetAtt SampleLambdaRole.Arn
SampleLambdaLiveAlias:
Type: AWS::Lambda::Alias
Properties:
FunctionName: !Ref SampleLambdaFunction
FunctionVersion: !GetAtt SampleLambdaGetMaxVersionFunction.version
Name: live
SampleLambdaFunctionScheduledEvent:
Type: AWS::Events::Rule
Properties:
State: ENABLED
ScheduleExpression: rate(1 minute) # same as cron(0/1 * * * ? *)
Description: Run SampleLambdaFunction once every 5 minutes.
Targets:
- Id: EventSampleLambda
Arn: SampleLambdaLiveAlias
Your error is in the last line of the piece of configuration you shared. In order to get the resource ARN you need to use Ref intrinsic function such as, !Ref SampleLambdaLiveAlias:
SampleLambdaFunctionScheduledEvent:
Type: AWS::Events::Rule
Properties:
State: ENABLED
ScheduleExpression: rate(1 minute) # same as cron(0/1 * * * ? *)
Description: Run SampleLambdaFunction once every 5 minutes.
Targets:
- Id: EventSampleLambda
Arn: !Ref SampleLambdaLiveAlias
Be aware that Ref intrinsic function may return different things for different types of resources. For Lambda alias it returns the ARN, just what you need.
You can check the official documentation for more detail.

InvalidResourceException when trying to "ref" an event in a function

I have a SAM template file that is throwing errors while doing sam build: [InvalidResourceException('MyFunction', "Type of property 'Events' is invalid.")]
First off, at the top of my file (at the same level as Globals) I have this event (the idea is to define a CloudWatch schedule that fires every 15 minutes and invokes a lambda):
Events:
Type: Schedule
Properties:
Schedule: rate(15 mins)
name: InvokeEvery15MinutesSchedule
Description: Invoke the target every 15 mins
Enabled: True
And here's what the function looks like:
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./path-to-code
Events:
- !Ref InvokeEvery15MinutesSchedule
I was doing this because I saw earlier that the following syntax is valid:
Globals:
Function:
Layers:
- !Ref Layer1
- !Ref Layer1
So, I thought that if I define an event at the top level and reference it inside the lambda, it will work. I want to keep it outside of the Lambda declaration because I want this to apply to several functions.
Can someone help with what I'm doing wrong?
"Events" is a lambda source object that defines the events that trigger this function. The object describing the source of events which trigger the function.
Try this:
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./path-to-code
Events:
RateSchedule:
Type: Schedule
Properties:
Schedule: rate(15 mins)
Name: InvokeEvery15MinutesSchedule
Description: Invoke the target every 15 mins
Enabled: True

How to show lambda function return values in Outputs of Cloudformation

I am looking for an option to list results of AWS::Lambda::Function in Outputs of Cloudformation.
Following is the snippet of cloudformation template for AWS:Lambda::Function
Resources:
AthenaLambdaFunction:
Type: 'AWS::Lambda::Function'
DeletionPolicy: Delete
DependsOn:
- IamRoleLambdaForAthena
Properties:
Code:
ZipFile: |
import boto3
import botocore
import os
ath = boto3.client('athena')
def handler(event, context):
outputBucket = os.environ.get("outputBucket")
QSTRING = 'select * from tableName limit 10'
response = ath.start_query_execution(QueryString=str(QSTRING), ResultConfiguration={'OutputLocation': outputBucket})
s3BucketOut = output_bucket + response['ResponseMetadata']['RequestId']
return s3BucketOut
Handler: index.handler
Runtime: python3.6
MemorySize: 128
Role: !GetAtt IamRoleLambdaForAthena.Arn
Timeout: 30
Environment:
Variables:
outputBucket: !Ref OutputS3Bucket
I want to show the value retuned by lambda function s3BucketOut in Outputs of Cloudformation. Something like below (off course, the code below doesn't work).
Outputs:
LambdaFunctionOutput:
Value: !Ref AthenaLambdaFunction.s3BucketOut
Description: Return Value of Lambda Function
Any suggestions please. TIA
You are half-way through it. With your code, you created the AWS Lambda function that you want to run. Now you need to make this function run on CloudFormation and capture its value. Note that you need to make small changes on your code to allow the value to be captured by CloudFormation.
The full code will be similar to this:
Resources:
AthenaLambdaFunction:
Type: 'AWS::Lambda::Function'
DeletionPolicy: Delete
DependsOn:
- IamRoleLambdaForAthena
Properties:
Code:
ZipFile: |
import boto3
import botocore
import os
import cfnresponse # this needs to be imported for replying to CloudFormation
ath = boto3.client('athena')
def handler(event, context):
outputBucket = os.environ.get("outputBucket")
QSTRING = 'select * from tableName limit 10'
response = ath.start_query_execution(QueryString=str(QSTRING), ResultConfiguration={'OutputLocation': outputBucket})
s3BucketOut = output_bucket + response['ResponseMetadata']['RequestId']
responseData = {} # added
responseData['S3BucketOut'] = s3BucketOut # added
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) # return modified
Handler: index.handler
Runtime: python3.6
MemorySize: 128
Role: !GetAtt IamRoleLambdaForAthena.Arn
Timeout: 30
Environment:
Variables:
outputBucket: !Ref OutputS3Bucket
S3BucketOutInvocation:
Type: Custom::S3BucketOut
Properties:
ServiceToken: !GetAtt AthenaLambdaFunction.Arn
Region: !Ref "AWS::Region"
Outputs:
LambdaFunctionOutput:
Value: !GetAtt S3BucketOutInvocation.S3BucketOut
Description: Return Value of Lambda Function
References:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html
What you can do is create what is called a "Lambda-backed Custom Resource" You would use this during Stack creation to get bits of information at creation type.
Further information can be found here
AWS Lambda-backed Custom Resources

Deploying Lambda with Cloud9

Hello here is my project structure:
-AppName
-Common
-common.js //Global module which i'm using in all functions
-Func1
-index.js
-Func2
-index.js
-template.yaml
And here is template.yaml content:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
Func1:
Type: 'AWS::Serverless::Function'
Properties:
Handler: Func1/index.handler
Runtime: nodejs6.10
MemorySize: 512
Timeout: 10
Func2:
Type: 'AWS::Serverless::Function'
Properties:
Handler: Func2/index.handler
Runtime: nodejs6.10
MemorySize: 512
Timeout: 10
When i deploy for example Func2, result package contain all folders inside application, instead only Func2. Is it possible to configure through yaml file, what files will included in result package?
For example if i deploy Func2 i want to see in package next:
-Common
-common.js
-Func2
-index.js
You can use the CodeUri key in SAM and point it to the folder where your code lies for that one function.
So for this function, you'd want to do:
Func2:
Type: 'AWS::Serverless::Function'
Properties:
CodeUri: Func2
Handler: index.handler
Runtime: nodejs6.10
MemorySize: 512
Timeout: 10

AWS Cloud Formation !Sub & !Ref functions inside AWS::Serverless::Function Policies

I have been using the !Sub function in my CloudFormation Yaml templates just fine. And when used it as an object property value it works for me
Object:
Property1: !Sub some-value-with-a-${variable}-in-it
The value of variable gets replaced as expected.
However, I can't figure out how to use the !Sub function in an element of a string array
Array:
- !Sub some-value-with-a-${variable}-in-it
That array element just gets ignored.
I am trying this in the context of a SAM template creating a AWS::Serverless::Function type resource. The Policies property can take an array of strings:
lambda:
Type: AWS::Serverless::Function
Properties:
CodeUri: api
FunctionName: !Sub api-${MyStageName}
Handler: Lambda:Api.Function::HandleAsync
Runtime: dotnetcore1.0
Policies:
- AWSLambdaBasicExecutionRole
- !Sub arn:aws:iam::${AWS::AccountId}:policy/some-policy
- arn:aws:iam::123456789:policy/another-policy
The !Sub function works in the FunctionName property in this example. But I only end up with 2 Policies attached to my generated role - AWSLambdaBasicExecutionRole and arn:aws:iam::123456789:policy/another-policy. The one including the !Sub function gets ignored.
I have tried options like putting the function on a new line:
Array:
-
!Sub some value with a ${variable} in it
Can anyone help?
Update
#Tom Melo pointed out that this is not a array problem so I have adjusted my question.
Further investigation has revealed it is not a Cloud Formation issue exactly, but very specific to the AWS::Serverless::Function resource type, and the Policies property within in. I suspect it has something to do with the fact that the Policies property is so flexible in what it can accept. It can accept strings referring to policy names or Arns, and it can also accept policy documents describing new policy. I suspect this means it is not able to support the functions.
Apparently, there's nothing wrong with !Sub function in a array of elements.
I have tried to create the following stack on Cloudformation and it worked:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'IAM Roles Template'
Parameters:
ArnBase:
Type: String
Default: arn:aws:iam::aws:policy/
AWSLambdaFullAccess:
Type: String
Default: AWSLambdaFullAccess
AmazonSESFullAccess:
Type: String
Default: AmazonSESFullAccess
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
-
PolicyName: CloudFormationFullAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "cloudformation:*"
Resource: "*"
ManagedPolicyArns:
- !Sub ${ArnBase}${AWSLambdaFullAccess}
- !Sub ${ArnBase}${AmazonSESFullAccess}
- !Sub arn:aws:iam::${AWS::AccountId}:policy/CustomAmazonGlacierReadOnlyAccess
That should work using SAM either...
Workaround
The AWS::Serverless::Function resource type supports several ways of configuring access.
Refer to policies directly via the Policies property and have the framework create a role that has those policies.
The Role property can refer to a role which already contains policies.
I was using option 1, but option 2 proves to be a way around the issues with !Sub function.
Creating a role explicitly with the policies we want using the AWS::IAM::Role means we can use the !Sub function within the ManagedPolicyArns property. For example
role:
Type: AWS::IAM::Role
Properties:
...
ManagedPolicyArns:
- !Sub arn:aws:iam::${AWS::AccountId}:policy/some-policy
...
lambda:
Type: AWS::Serverless::Function
Properties:
...
Role: !GetAtt role.Arn
...

Resources