How use Sub function wth GetAtt in serverless? - amazon-web-services

Consider the following code:
EventRule:
Type: AWS::Events::Rule
Properties:
Description: "ny trigger"
ScheduleExpression: "rate(1 minute)"
State: "DISABLED"
Targets:
-
Arn: !GetAtt MyFunctionLambdaFunction.Arn
Id: "someId"
Input:
Fn::Sub:
- '{"arn":"#{arn}"}'
- arn: !GetAtt MyStateMachine.Arn
This gives me en error:
Error: The CloudFormation template is invalid: Template error: One or more Fn::Sub
intrinsic functions don't specify expected arguments. Specify a string as first argument,
and an optional second argument to specify a mapping of values to replace in the string

You don't need GetAtt in Sub here. Sub can resolve things like MyStateMachine.Arn. So you can just do something like:
Input: !Sub
- |-
{ "arn": "${MyStateMachine.Arn}" }
In some cases you might still need to pass in parameters (for instance when you need to call functions like ImportValue). For instance, if your state machine is defined in a different stack and is exported via other-stack-MyStateMachine-Arn output, you can do the following:
Input: !Sub
- |-
{ "arn": "${MyStateMachineArn}" }
- MyStateMachineArn: !ImportValue
'Fn::Sub': 'other-stack-MyStateMachine-Arn'

Related

Template validation failed when using SAM syntax instead of CloudFormation syntax for step function

I have the following step function in my AWS SAM template, it was defined using the syntax in the documentation. I'm using intrinsic functions to get some pseudoparameters but something is going wrong with them.
SfdcOrderEventsStepFunction:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionSubstitutions:
Region: !Ref "AWS::Region"
AccountId: !Ref "AWS::AccountId"
EventBusPublishTarget: "order-events"
DefinitionUri: sfn-definition.asl.yml
Events:
EventBridgeEvent:
Type: EventBridgeRule
Properties:
EventBusName: sfdc-events
Pattern:
# TODO: Update pattern when the salesforce service is ready
source:
- prefix: salesforce.com
detail-type:
- Order
detail:
Payload__c:
attributes:
type:
- order
InputPath: $.detail
Name: sfdc-order-events
Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/stepfunction_execution_role'
Tracing:
Enabled: true
when I try to deploy it shows me the following error:
Resource template validation failed for resource
SfdcOrderEventsStepFunction as the template has invalid properties.
Please refer to the resource documentation to fix the template.
Properties validation failed for resource SfdcOrderEventsStepFunction
with message:
#/De finitionSubstitutions/ AccountId: 2 subschemas matched instead of one
#/DefinitionSubstitutions/AccountId: expected type: Boolean, found: String
At the end it deploys without problems. The step function and all of its components run as expected and without errors, but I wanted to know if there if something I can do to fix the template.

How to Pass Multiple URLs to AllowOrigins in CloudFormation Template

I am using a CloudFormation template in YML format.
Depending on the environment, I need to be able to use different URLs for the Allowed Origins attribute of my CorsConfiguration. Ideally, I would like to use a Parameter defined like this:
AllowedOrigins:
Description: Allowed Origins
Type: String
AllowedPattern: '.+'
I have tried to pass in a delimited string (i.e. "http://localhost:4200,http://localhost:4201"), and split the values like this:
OnboardingHttpApi:
Type: AWS::Serverless::HttpApi
Properties:
CorsConfiguration:
AllowOrigins: !Split [ ",", !Ref AllowedOrigins ]
The response in CloudFormation is:
Warnings found during import: CORS Scheme is malformed, ignoring. (Service: AmazonApiGatewayV2; Status Code: 400; Error Code: BadRequestException; Request ID: 21072c02-70c3-473d-9629-784005226bd4; Proxy: null) (Service: null; Status Code: 404; Error Code: BadRequestException; Request ID: null; Proxy: null)
This is the answer I got from AWS Support:
The Split function is for splitting strings into a list, but it is not for referencing an attribute. It is designed to be used with the Select function or other functions. So it is not a stand-alone function to be used for referencing. For this, you can use the CommaDelimitedList parameter type. You can use the CommaDelimitedList parameter type to specify multiple string values in a single parameter. Once you pass the CommaDelimitedList parameter value, you can reference it later on your template. Here is a CloudFormation template that works:
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Parameters:
AllowedOriginsURLs:
Type: CommaDelimitedList
Default: 'https://example.com,https://example2.com'
Description: Please enter your URLs
Resources:
HttpApi:
Type: 'AWS::Serverless::HttpApi'
Properties:
StageName: my-stage-name
Tags:
Tag: MyTag
StageVariables:
StageVar: Value
CorsConfiguration:
AllowOrigins: !Ref AllowedOriginsURLs
AllowHeaders: [ x-apigateway-header ]
AllowMethods: [ GET ]
MaxAge: 600
AllowCredentials: true
The AllowedOriginsURLs parameter is of type CommaDelimitedList, with the default being 'http://localhost:4200,http://localhost:4201'. You can change this parameter on startup, then you can reference AllowedOriginsURLs on AllowOrigins.

"Parameter values specified for a template which does not require them." when trying to deploy a conformance pack via AWS cloudformation

I am working on a proof of concept for deploying a conformance pack via AWS cloudformation and I am stumped by the error "Parameter values specified for a template which does not require them." The config rule I am using does require a parameter. Code is attached. I have also tested the template with cfn-lint and do not receive any feedback/errors.
My template is "simple" and below:
Parameters:
ElbPredefinedSecurityPolicySslCheckParamPredefinedPolicyName:
Default: ELBSecurityPolicy-2016-08
Type: String
Resources:
TestingConformancePack:
Type: AWS::Config::ConformancePack
Properties:
ConformancePackName: TestCP
ConformancePackInputParameters:
-
ParameterName: PredefinedPolicyName
ParameterValue: !Ref ElbPredefinedSecurityPolicySslCheckParamPredefinedPolicyName
TemplateBody: |
Resources:
ElbPredefinedSecurityPolicySslCheck:
Properties:
ConfigRuleName: elb-predefined-security-policy-ssl-check
InputParameters:
predefinedPolicyName:
Ref: ElbPredefinedSecurityPolicySslCheckParamPredefinedPolicyName
Scope:
ComplianceResourceTypes:
- AWS::ElasticLoadBalancing::LoadBalancer
Source:
Owner: AWS
SourceIdentifier: ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK
Type: AWS::Config::ConfigRule
The cause is that you are passing a parameter (the one specified in ConformancePackInputParameters) to a CloudFormation template (the one specified in TemplateBody) that does not contain a Parameters section and therefore expects no parameters. To solve this, you need to add a parameter to the inner CloudFormation template, which you can then refer to in predefinedPolicyName:
The following template works for me:
Parameters:
ElbPredefinedSecurityPolicySslCheckParamPredefinedPolicyName:
Default: ELBSecurityPolicy-2016-08
Type: String
Resources:
TestingConformancePack:
Type: AWS::Config::ConformancePack
Properties:
ConformancePackName: TestCP
ConformancePackInputParameters:
-
ParameterName: PredefinedPolicyName
ParameterValue: !Ref ElbPredefinedSecurityPolicySslCheckParamPredefinedPolicyName
TemplateBody: |
Parameters:
PredefinedPolicyName:
Type: String
Resources:
ElbPredefinedSecurityPolicySslCheck:
Properties:
ConfigRuleName: elb-predefined-security-policy-ssl-check
InputParameters:
predefinedPolicyName:
Ref: PredefinedPolicyName
Scope:
ComplianceResourceTypes:
- AWS::ElasticLoadBalancing::LoadBalancer
Source:
Owner: AWS
SourceIdentifier: ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK
Type: AWS::Config::ConfigRule
I was making a test case resource using cloudformation and stumbled upon this same error.
“Parameter values specified for a template which does not require them.”
Since it was a test case, I didn't use any parameters at all. The above answer was helpful for me to understand it has to do something with parameters. Even though you are not using any, there are some parameters passed while deploying the cfn.
By default, cloudformation also sends env as parameter which needs to come under parameters as such. (Below code snippet in JSON)
"Parameters": {
"env": {
"Type": "String"
}
},
Hope this was helpful.

How to get the ARN of an SSM Document in CloudFormation?

I have a CloudFormation template that creates an AWS::Events::Rule and an AWS::SSM::Document. I need to provide a list of Targets for the SSM::Rule, but each target expects an ARN:
mySSMDocument:
Type: AWS::SSM::Document
Properties:
DocumentType: 'Command'
Content:
schemaVersion: '2.2'
description: "Code that will be run on EC2"
mainSteps:
- action: "aws:runShellScript"
name: runShellScript
inputs:
runCommand:
- 'Some command to execute'
myEventRule:
Type: AWS::Events::Rule
Properties:
Description: "A description for the Rule."
EventPattern:
source:
- "aws.autoscaling"
detail-type:
- "EC2 Instance-terminate Lifecycle Action"
detail:
AutoScalingGroupName:
- !Ref 'someAutoScalingGroupInThisTemplate'
RoleArn: 'some role ARN'
State: "ENABLED"
Targets:
- Id: "some-unique-id"
Arn: <-- This is the value that I need to fill in.
RunCommandParameters:
RunCommandTargets:
- Key: "tag: Name"
Values:
- 'The name of the EC2 machine'
I think that I need to replace the <-- This is the value that I need to fill in. with the ARN of mySSMDocument, but I don't see any way to retrieve this value from within the template itself. The documentation does not specify any GetAtt functionality on SSM::Document that allows to get the ARN. Anyone know how to solve this issue?
This is ARN pattern of Document
arn:${Partition}:ssm:${Region}:${Account}:document/${DocumentName}
example:
arn:aws:ssm:us-east-2:12345678912:document/demoooo
You can use Ref function to get name of document, then Sub to create final ARN
refer: https://docs.aws.amazon.com/IAM/latest/UserGuide/list_awssystemsmanager.html#awssystemsmanager-resources-for-iam-policies
!Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:document/${mySSMDocument}
You can produce the ARN format for AWS::SSM::Document using the return Value for AWS::SSM::Document, the Pseudo Parameters for Partition, Region, and AccountId, and the Sub intrinsic function

Getting an ARN for an imported Kinesis stream

I have two nested Cloudformation stacks - the first template needs to define a Kinesis stream, the second needs to use a reference to that stream's ARN, as an argument to a further nested stack.
So it seems I need to "export" the stream from the first template, and "import" it into the second (following AWS docs on importing/exporting values across stacks) -
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
My export code [truncated] looks like this -
Outputs:
MyKinesisStreamOutput:
Value:
Ref: MyKinesisStream
Export:
Name: my-kinesis-stream
Resources:
MyKinesisStream:
Properties:
Name:
Ref: AppName
ShardCount: 1
Type: AWS::Kinesis::Stream
Whilst my import code [truncated] looks like this -
MyNestedStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub "https://s3.${AWS::Region}.amazonaws.com/my-nested-stack.yaml"
Parameters:
AppName: my-nested-stack
KinesisStream:
Fn::GetAtt:
- Fn::ImportValue:
my-kinesis-stream
- Arn
But then I get the following Cloudformation error -
Template error: every Fn::GetAtt object requires two non-empty parameters, the resource name and the resource attribute
and suspect I am falling foul of this -
For the Fn::GetAtt logical resource name, you cannot use functions. You must specify a string that is a resource's logical ID.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
Assuming I am exporting and importing the Kinesis stream correctly, how then am I supposed to get its Arn value ?
When you export something in Outputs, it's just a string, you can no longer GetAtt on it in the importing template.
What you need to do is to additionally export ARN:
Outputs:
MyKinesisStream:
Value: !Ref MyKinesisStream
Export:
Name: my-kinesis-stream
MyKinesisStreamArn:
Value: !GetAtt MyKinesisStream.Arn
Export:
Name: my-kinesis-stream-arn
I can think of two potential ways:
1) Fn::GetAtt:[!ImportValue my-kinesis-stream, Arn]
Sorry wasn't reading carefully enough
or what I would prefer
2) Directly export the required value as output: Value: !GetAtt MyKinesisStream.Arn
Hope that helps!