CloudFormation Transform::Include parameters - amazon-ec2

I want to use AWS macro Transform::Include with some dynamic parameters for my file.
Resources:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
TestMacroVariable:
Default: 2
Type: Number
Location: !Sub "s3://${InstallBucketName}/test.yaml"
test.yaml:
DataAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
LaunchConfigurationName:
Ref: DataLaunchConfiguration
MinSize: '1'
MaxSize: '100'
DesiredCapacity:
Ref: TestMacroVariable
...
After calling: aws cloudformation describe-stack-events --stack-name $stack
I get:
"ResourceStatusReason": "The value of parameter TestMacroVariable
under transform Include must resolve to a string, number, boolean or a
list of any of these.. Rollback requested by user."
When I try to do it this way:
Resources:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
TestMacroVariable: 2
Location: !Sub "s3://${InstallBucketName}/test.yaml"
I get:
"ResourceStatusReason": "Template format error: Unresolved resource
dependencies [TestMacroVariable] in the Resources block of the
template"
Error is the same when I don't provide TestMacroVariable at all.
Tried with different types: String, Number, Boolean, List - none of them work.

As i know you cannot have anything other than Location key in the Parameters section of the AWS::Include. Check here AWS DOC

As an alternative, you can pass in the whole S3 path as a parameter and reference it in Location:
Parameters:
MyS3Path:
Type: String
Default: 's3://my-cf-templates/my-include.yaml'
...
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: !Ref MyS3Path

Building on what #BatteryAcid Said you can refer the parameters in your Cloudformation template directly from your file using Sub function:
In your CF template :
Parameters:
TableName:
Type: String
Description: Table Name of the Dynamo DB Users table
Default: 'Users'
In the file you are including:
"Resource": [
{
"Fn::Sub": [
"arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}",
{
"tableName": {
"Ref": "TableName"
}
}
]
}
Alternatively doesn't have to be a parameter but any resource from your template :
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${QueryTelemtryFunction.Arn}/invocations

Related

response mapping template for a javascript resolver

I have resolvers written in JS and not VTL since AWS added support for it.
I am using AWS SAM template and this is how it looks
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
RequestMappingTemplate: |
{
"operation": "PutItem",
"key": util.dynamodb.toMapValues({"userId": ctx.userId, "sortKey": ctx.sortKey}),
"attributeValues": util.dynamodb.toMapValues(ctx),
}
ResponseMappingTemplate: "ctx.result"
But when I query the mutation on the Appsync console I get the following error
Unrecognized token 'util': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\
I tried a couple of variations where I passed the entire value as string but it didn't work.
What am I missing or doing wrong in this template mapping?
Edit - My Updated Answer :
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
Code: |
import { util } from '#aws-appsync/utils';
export function request(ctx) {
return {
operation: 'PutItem',
key: util.dynamodb.toMapValues({"userId":
ctx.userId, "sortkey": ctx.sortKey}),
attributeValues: util.dynamodb.toMapValues(ctx),
}
}
export function response(ctx) {
const { error, result } = ctx;
if (error) {
return util.appendError(error.message, error.type, result);
}
return ctx.result;
}
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
Think JS resolvers are only available in Pipeline Resolvers currently and not Unit Resolvers. Also, in a Pipeline Resolver you may need to add the runtime in someway that is similar to below. Without it it may be defaulting to VTL. Note, not tested this in SAM just guessing based off of Cloudformation and CDK docs.
AWS::AppSync::Resolver
AWS::AppSync::Resolver Runtime
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
RequestMappingTemplate: |
{
"operation": "PutItem",
"key": util.dynamodb.toMapValues({"userId": ctx.noteId, "selection": sk}),
"attributeValues": util.dynamodb.toMapValues(ctx),
}
ResponseMappingTemplate: "ctx.result"
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0

Adding IoT rule with an error action through cloudformation yaml

I am trying to add an iot rule with error action using following cloudformation yaml file
extract from yaml:
DaIoTRule:
Type: AWS::IoT::TopicRule
Properties:
RuleName: sda
TopicRulePayload:
RuleDisabled: false
Sql: SELECT *, topic(2) AS source FROM 'topic/sensors/+'
Actions:
- Lambda:
FunctionArn: !GetAtt LambdaFunction.Arn
ErrorAction:
S3:
RoleArn: !GetAtt DAIoTRuleErrorActionIamRole.Arn
Bucket: iot-message-dump
Key: "errors/${topic()}/${timestamp()}"
But I keep getting this error:
{
"StackId": "arn:aws:cloudformation:us-east-1:961234632786:stack/wx-da-lambda/91423s00-4e97-11ea-aedd-0ee829hbc650",
"EventId": "DAIoTRule-CREATE_FAILED-2020-02-17T06:51:38.299Z",
"StackName": "da-lambda",
"LogicalResourceId": "DaIoTRule",
"PhysicalResourceId": "",
"ResourceType": "AWS::IoT::TopicRule",
"Timestamp": "2020-02-17T06:51:38.299Z",
"ResourceStatus": "CREATE_FAILED",
"ResourceStatusReason": "Encountered unsupported property bucket"
}
I created this yaml using https://docs.aws.amazon.com/iot/latest/developerguide/rule-error-handling.html as reference.
Can someone point me to what I am doing wrong ?
CloudFormation uses the BucketName property instead of Bucket for S3 actions (including error actions).
See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-topicrule-s3action.html#cfn-iot-topicrule-s3action-bucketname

How do you "DependsOn" a Lambda function from a CFN resource?

In Serverless, I am creating an AWS::Lambda::Permission CFN resource that allows Cognito to invoke a Custom Message Lambda Handler.
AWS::Lambda::Permission depends on the lambda. How do I ensure that the lambda is created first?
I have already tried adding a DependsOn property to the AWS::Lambda::Permission CFN resource with no luck.
Below is my CFN resource that is trying to add permissions to Cognito to invoke a lambda:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
Principal: cognito-idp.amazonaws.com
FunctionName: arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-cognitoCustomMessage
SourceArn: arn:aws:cognito-idp:${self:provider.region}:#{AWS::AccountId}:userpool/${self:provider.environment.USER_POOL_ID}
Here is what my lambda looks like in my serverless.yml:
cognitoCustomMessage:
handler: src/main/lambdas/users_handler.cognitoCustomMessage
Here is what my lambda is doing on a very basic level:
cognitoCustomMessage(event, next) {
if (event.triggerSource === 'CustomMessage_ForgotPassword') {
// do stuff
}
return next(null, event);
}
The error I get from the above is:
An error occurred: CognitoCustomMessageLambdaFunction - Function not
found:
arn:aws:lambda:us-west-2:1234567890:my-service-dev-cognitoCustomMessage
(Service: AWSLambdaInternal; Status Code: 404; Error Code:
ResourceNotFoundException; Request ID:
e2a98525-5090-4d0f-a1f5-20610474f93b).
If I add a DependsOn:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-cognitoCustomMessage
...
....
The error I get from above is:
The CloudFormation template is invalid: Template format error:
DependsOn must be a string or list of strings.
I have also tried:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: CognitoCustomMessageLambdaFunction
...
....
The error I get from above is:
An error occurred: CognitoCustomMessageLambdaFunction - Function not
found:
arn:aws:lambda:us-west-2:1234567890:my-service-dev-cognitoCustomMessage
(Service: AWSLambdaInternal; Status Code: 404; Error Code:
ResourceNotFoundException; Request ID:
b888ae82-a0d7-4d69-888e-9e63027925c1).
I expect that there should be some method to create a lambda function first prior to the CFN resource needing to use it, but this does not seem to be the case with DependsOn.
The DependsOn attribute should have the logical name of the Lambda Function in the Cloud formation template not the ARN of Lambda Function. For example, if your Lambda function's logical name in the Cloud Formation template is MyLambda then DependsOn should be like this:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: MyLambda
NOTE: open cloud formation template .serverless/cloudformation-template-update-stack.json and look for logical lambda function name.
Example:
{
"MonitorLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "deploy-dev",
"S3Key": "serverless/dev/1641551717730-2022-01-07T10:35:17.730Z/zip"
},
"Handler": "src/monitors/handler.devMonitor",
"Runtime": "nodejs14.x",
"FunctionName": "dev-monitor",
"MemorySize": 1024,
"Timeout": 6,
"Environment": {
"Variables": {
"STAGE": "dev",
}
},
"Role": {
"Fn::GetAtt": [
"monitorIamRoleLambdaExecution",
"Arn"
]
}
}
}
MonitorLambdaFunction is the name you are looking for.
Your function defined in serverless.yml is converted into a cloudformation resource under the hood. The resource is called XLambdaFunction where X = The name of your function with the first letter capitalized.
So if you have:
functions:
hello:
handler: handler.hello
...other function stuff...
You can reference:
DependsOn: HelloLambdaFunction
I can only assume that if your function is already named CognitoCustomMessageLambdaFunction then you would have to reference:
DependsOn: CognitoCustomMessageLambdaFunctionLambdaFunction
I faced similar issue, adding dependsOn key worked for me.

How to Access Athena QueryString From CloudFormation in Lambda?

AWS-loaded question, but does anyone know what the proper way to access an Athena Query String (in CloudFormation) in Lambda?
I have set up both the Athena NamedQuery and the Lambda in CloudFormation. Abstracting out some of the more project-specific details, the general form I have is:
MyQuery:
Type: AWS::Athena::NamedQuery
Properties:
Database: "mydatabase"
Name: "DataQuery"
QueryString: SELECT * FROM mydatabase
MyLambda:
Type: AWS::Serverless::Function
Properties:
Handler: 'handlers.migration_handler'
Runtime: python3.6
CodeUri:
Bucket: BATS::SAM::CodeS3Bucket
Key: BATS::SAM::CodeS3Key
Description: Migrates data from output of Athena query to S3
Policies:
- AmazonS3FullAccess
- AWSLambdaExecute
- AmazonAthenaFullAccess
Environment:
Variables:
MY_QUERY:
Ref: MyQuery
When I'm writing the handler for the lambda, I want to call:
athena_client = boto3.client('athena')
response = athena_client.start_query_execution(
QueryString = os.environ['MY_QUERY']
ResultConfiguration = {'OutputLocation: 's3://my-bucket'}
)
However, QueryString needs to be a string, so this currently isn't working. I want to access the QueryString property in MY_QUERY, and I feel like I'm so close but I'm not quite sure how to get that last step. Any help here would be greatly appreciated.
Figured it out yesterday (or more specifically, my teammate figured it out).
Boto3 happens to have another method called get_named_query(NamedQueryId), and that returns a dictionary in the form of:
{
'NamedQuery': {
'Name': 'string',
'Description': 'string',
'Database': 'string',
'QueryString': 'string',
'NamedQueryId': 'string'
}
Thus, my code worked when I modified my lambda handler to:
athena_client = boto3.client('athena')
query_info = athena_client.get_named_query(
NamedQueryId = os.environ['MY_QUERY']
)
response = athena_client.start_query_execution(
QueryString = query_info['NamedQuery']['QueryString']
ResultConfiguration = {'OutputLocation: 's3://my-bucket'}
)

How to reuse example from another definition in swagger?

I have a definition of a report object. I have another definition of reports object that has an array of report objects (via a $ref).
In the report definition, I have an example defined, which works fine in swagger UI.
In the reports definition, I want it to use the example from the report definition.
How can I do this? I've tried several things using $ref, the closest I got was what I have in the following YAML...
definitions:
report:
type: object
properties:
ID:
type: number
format: int
description: "DB record ID of the report."
readOnly: true
ErrorContent:
type: string
description: "The actual problem or error infomation for this report. This can be exception stack, etc."
readOnly: true
UserComments:
type: string
description: "Any user comments collected by the app and submitted with the report."
readOnly: true
ReportedBy:
type: string
description: "The person using the app when it triggered the error this report is for."
readOnly: true
ReportedDateTime:
type: string
description: "The date/time the report was submitted."
readOnly: true
required:
- ID
- ErrorContent
- ErrorType
- UserComments
- ReportedBy
- ReportedDateTime
example:
ID: 11367
ErrorContent: "Operation is not valid due to the current state of the object."
ErrorType: "Exception"
UserComments: "Was clicking this and that and then Boom!"
ReportedBy: "domain\\name"
ReportedDateTime: "2016-01-19 14:07:00"
reports:
properties:
message:
type: string
reports:
type: array
items:
$ref: '#/definitions/report'
example:
message: "success"
reports:
- $ref: '#/definitions/report'
However, in Swagger UI, the above results in...
{
"message": "success",
"reports": [
{
"$ref": "#/definitions/report"
}
]
}
One interesting note, in Swagger UI, when I look at the model view, it does have all of report even with descriptions.
This behavior is correct--the examples section cannot be referenced with a JSON pointer. See the OAI Specification for what is currently supported.
If you feel this is a common use case, please open an issue on the spec repository.

Resources