I am trying to create an AWS CloudFormation template using YAML. I add a UserPool resource as follows. The user pool name & id should be obtained via a parameter value i.e., if the value of parameter paramUserPoolName is 'Sample', then:
UserPoolName = Sample
UserPool Resource Name = SampleUserPool i.e., concatenated value of 'paramUserPoolName + UserPool'
Parameters:
paramUserPoolName:
Type: String
Resources:
<I need 'paramUserPoolName + UserPool' here >:
Type: 'AWS::Cognito::UserPool'
Properties: {
"UserPoolName": paramUserPoolName
}
How can I dynamically create a resource id in CloudFormation template?
PS:
The following worked:
Resources:
SampleUserPool:
Type: 'AWS::Cognito::UserPool'
Properties:
UserPoolName: !Sub ${paramUserPoolName}UserPool
Use !Sub for that. You can also use !Join, but !Sub is easier.
Parameters:
paramUserPoolName:
Type: String
Resources:
Type: 'AWS::Cognito::UserPool'
Properties:
UserPoolName: !Sub ${paramUserPoolName}UserPool
Related
Please help me with the I'm using to create two ALB with the exception if a value is put in one of the parameters then will create an alb that has the instances id but if I pass two instances id in the parameter ELB1InstanceIds then it gave me an error:
Properties validation failed for resource HTTPTG1 with message: #/Targets/0/Id: expected type: String, found: JSONArray
earlier I was using parameter `Type: 'ListAWS::EC2::Instance::Id' but when i don't select any instance in ELB2InstanceIds then I got an error that it required a selected value then only stack can be run.
so I have created with the type string and passed an instance id, it is working with one instance id but not working when I specify two instances id in one of the parameters.
Parameters:
ELB1InstanceIds:
Type: String
Default: i-12345678
ELB2InstanceIds:
Type: String
Default: i-12345678
Conditions:
IsELB1InstanceIdsNotEmpty: !Not [!Equals [!Ref ELB1InstanceIds, "i-12345678"]]
IsELB2InstanceIdsNotEmpty: !Not [!Equals [!Ref ELB2InstanceIds, "i-12345678"]]
Resources:
HTTPTG1:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
Targets:
- Id: !If
- IsELB1InstanceIdsNotEmpty
- - !Split [",", !Ref ELB1InstanceIds]
- !Ref 'AWS::NoValue'
HTTPTG2:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
Targets:
- Id: !If
- IsELB2InstanceIdsNotEmpty
- - !Split [",", !Ref ELB2InstanceIds]
- !Ref 'AWS::NoValue'
earlier I was using two parameters ELB1InstanceIds, and ELB2InstanceIds with Type: 'List<AWS::EC2::Instance::Id>' but when I don't select any instance in ELB2InstanceIds then I got an error that it required a selected value then only stack can be run.
I have to add taskdefintion entry based on condition. Below is the template i created.
Conditions
IsProdEnv: !Equals [ !Ref envtype, "prod" ]
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
EntryPoint:
- !If [IsProdEnv, !Split [",", ["python3", "hello.py"]], "./script.sh"]
I'm getting
Template error: every Fn::Split object requires two parameters, (1) a string delimiter and (2) a string to be split or a function that returns a string to be split.
You have two strings instead of one: "python3" and "hello.py". You need to have one string, e.g. "python3, hello.py". How this string looks, depends on your use-case.
I am trying to create a LaunchTemplate, which references an IamInstanceProfile, in my Cloudformation stack. Here is the code- i have omitted the irrelevant parts:
...
Resources:
ServerLaunchTemplate:
Type: 'AWS::EC2::LaunchTemplate'
Properties:
LaunchTemplateData:
InstanceType: !Ref InstanceType
SecurityGroups:
- !Ref SecGroup
IamInstanceProfile: !Ref ServerProfile
UserData:
...
ServerProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
Roles:
- !Ref ServerRole
...
The ServerProfile gets created successfully. However when the stack creation process reaches the step of creating the ServerLaunchTemplate, it fails with the error:
Property validation failure: [Value of property {/LaunchTemplateData/IamInstanceProfile} does not match type {Object}]
If i omit the reference to the IamInstanceProfile, the LaunchTemplate get created successfully.
According to the documentation and some examples this should work... Based on the error i understand, that the InstanceType field of the LaunchTemplate needs to reference an object, but "!Ref InstanceType" returns the resource id.
How can i fix this? How could i retrieve the object, that is presumably required by the "/LaunchTemplateData/IamInstanceProfile" field?
Thank you
Easy to miss in the docs: IamInstanceProfile requires an IamInstanceProfile Cloudformation object with the Arn of the referenced IamInstanceProfile being a property of it.
See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-iaminstanceprofile and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-iaminstanceprofile.html.
This should work:
PortalLaunchTemplate:
Type: 'AWS::EC2::LaunchTemplate'
Properties:
LaunchTemplateName: !Sub ${InstanceName}-launch-template
LaunchTemplateData:
ImageId: !Ref AmiId
...
IamInstanceProfile:
Arn: !GetAtt InstanceProfile.Arn
What worked for me was:
"IamInstanceProfile":{ "Arn":
{
"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:instanceprofile/${name_of_Instance_profile}"
}
Presumably because it requires the "Arn" value as a string.
This is very similar to questions/44786412 but mine appears to be triggered by YAML safe_load(). I'm using Ruamel's library and YamlReader to glue a bunch of CloudFormation pieces together into a single, merged template. Is bang-notation just not proper YAML?
Outputs:
Vpc:
Value: !Ref vpc
Export:
Name: !Sub "${AWS::StackName}-Vpc"
No problem with these
Outputs:
Vpc:
Value:
Ref: vpc
Export:
Name:
Fn::Sub: "${AWS::StackName}-Vpc"
Resources:
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock:
Fn::FindInMap: [ CidrBlock, !Ref "AWS::Region", Vpc ]
Part 2; how to get load() to leave what's right of the 'Fn::Select:' alone.
FromPort:
Fn::Select: [ 0, Fn::FindInMap: [ Service, https, Ports ] ]
gets converted to this, that now CF doesn't like.
FromPort:
Fn::Select: [0, {Fn::FindInMap: [Service, https, Ports]}]
If I unroll the statement fully then no problems. I guess the shorthand is just problematic.
FromPort:
Fn::Select:
- 0
- Fn::FindInMap: [Service, ssh, Ports]
Your "bang notation" is proper YAML, normally this is called a tag. If you want to use the safe_load() with those you'll have to provide constructors for the !Ref and !Sub tags, e.g. using:
ruamel.yaml.add_constructor(u'!Ref', your_ref_constructor, constructor=ruamel.yaml.SafeConstructor)
where for both tags you should expect to handle scalars a value. and not the more common mapping.
I recommend you use the RoundTripLoader instead of the SafeLoader, that will preserve order, comments, etc. as well. The RoundTripLoader is a subclass of the SafeLoader.
If you are using ruamel.yaml>=0.15.33, which supports round-tripping scalars, you can do (using the new ruamel.yaml API):
import sys
from ruamel.yaml import YAML
yaml = YAML()
yaml.preserve_quotes = True
data = yaml.load("""\
Outputs:
Vpc:
Value: !Ref: vpc # first tag
Export:
Name: !Sub "${AWS::StackName}-Vpc" # second tag
""")
yaml.dump(data, sys.stdout)
to get:
Outputs:
Vpc:
Value: !Ref: vpc # first tag
Export:
Name: !Sub "${AWS::StackName}-Vpc" # second tag
In older 0.15.X versions, you'll have to specify the classes for the scalar objects yourself. This is cumbersome, if you have many objects, but allows for additional functionality:
import sys
from ruamel.yaml import YAML
class Ref:
yaml_tag = u'!Ref:'
def __init__(self, value, style=None):
self.value = value
self.style = style
#classmethod
def to_yaml(cls, representer, node):
return representer.represent_scalar(cls.yaml_tag,
u'{.value}'.format(node), node.style)
#classmethod
def from_yaml(cls, constructor, node):
return cls(node.value, node.style)
def __iadd__(self, v):
self.value += str(v)
return self
class Sub:
yaml_tag = u'!Sub'
def __init__(self, value, style=None):
self.value = value
self.style = style
#classmethod
def to_yaml(cls, representer, node):
return representer.represent_scalar(cls.yaml_tag,
u'{.value}'.format(node), node.style)
#classmethod
def from_yaml(cls, constructor, node):
return cls(node.value, node.style)
yaml = YAML(typ='rt')
yaml.register_class(Ref)
yaml.register_class(Sub)
data = yaml.load("""\
Outputs:
Vpc:
Value: !Ref: vpc # first tag
Export:
Name: !Sub "${AWS::StackName}-Vpc" # second tag
""")
data['Outputs']['Vpc']['Value'] += '123'
yaml.dump(data, sys.stdout)
which gives:
Outputs:
Vpc:
Value: !Ref: vpc123 # first tag
Export:
Name: !Sub "${AWS::StackName}-Vpc" # second tag
I'm attempting to export a DynamoDb StreamArn from a stack created in CloudFormation, then reference the export using !ImportValue in the serverless.yml.
But I'm getting this error message:
unknown tag !<!ImportValue> in "/codebuild/output/src/serverless.yml"
The cloudformation and serverless.yml are defined as below. Any help appreciated.
StackA.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Resources for the registration site
Resources:
ClientTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
Properties:
TableName: client
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
Outputs:
ClientTableStreamArn:
Description: The ARN for My ClientTable Stream
Value: !GetAtt ClientTable.StreamArn
Export:
Name: my-client-table-stream-arn
serverless.yml
service: my-service
frameworkVersion: ">=1.1.0 <2.0.0"
provider:
name: aws
runtime: nodejs6.10
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeStream
- dynamodb:GetRecords
- dynamodb:GetShardIterator
- dynamodb:ListStreams
- dynamodb:GetItem
- dynamodb:PutItem
Resource: arn:aws:dynamodb:*:*:table/client
functions:
foo:
handler: foo.main
events:
- stream:
type: dynamodb
arn: !ImportValue my-client-table-stream-arn
batchSize: 1
Solved by using ${cf:stackName.outputKey}
I struggled with this as well, and what did trick for me was:
functions:
foo:
handler: foo.main
events:
- stream:
type: dynamodb
arn:
!ImportValue my-client-table-stream-arn
batchSize: 1
Note, that intrinsic functions ImportValue is on a new line and indented, otherwise the whole event is ignored when cloudformation-template-update-stack.json is generated.
It appears that you're using the !ImportValue shorthand for CloudFormation YAML. My understanding is that when CloudFormation parses the YAML, and !ImportValue actually aliases Fn::ImportValue. According to the Serverless Function documentation, it appears that they should support the Fn::ImportValue form of imports.
Based on the documentation for Fn::ImportValue, you should be able to reference the your export like
- stream:
type: dynamodb
arn: {"Fn::ImportValue": "my-client-table-stream-arn"}
batchSize: 1
Hope that helps solve your issue.
I couldn't find it clearly documented anywhere but what seemed to resolve the issue for me is:
For the Variables which need to be exposed/exported in outputs, they must have an "Export" property with a "Name" sub-property:
In serverless.ts
resources: {
Resources: resources["Resources"],
Outputs: {
// For eventbus
EventBusName: {
Export: {
Name: "${self:service}-${self:provider.stage}-UNIQUE_EVENTBUS_NAME",
},
Value: {
Ref: "UNIQUE_EVENTBUS_NAME",
},
},
// For something like sqs, or anything else, would be the same
IDVerifyQueueARN: {
Export: {
Name: "${self:service}-${self:provider.stage}-UNIQUE_SQS_NAME",
},
Value: { "Fn::GetAtt": ["UNIQUE_SQS_NAME", "Arn"] },
}
},
}
Once this is deployed you can check if the exports are present by running in the terminal (using your associated aws credentials):
aws cloudformation list-exports
Then there should be a Name property in a list:
{
"ExportingStackId": "***",
"Name": "${self:service}-${self:provider.stage}-UNIQUE_EVENTBUS_NAME", <-- same as given above (but will be populated with your service and stage)
"Value": "***"
}
And then if the above is successful, you can reference it with "Fn::ImportValue" like so, e.g.:
"Resource": {
"Fn::ImportValue": "${self:service}-${self:provider.stage}-UNIQUE_EVENTBUS_NAME", <-- same as given above (but will be populated with your service and stage)
}