Name the resources with the supplied parameters to cloud formation stack - amazon-web-services

Im new to cloudformation and intrinsc function.
I am trying to run a stack with the template which accepts ent type as parameter
Using this parameter I would like to name my s3 bucket.
But Im getting 400 Bad request
My template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Creates an S3 bucket using parameters supplied.",
"Parameters": {
"AssetInsightId": {
"Description": "Asset Insight ID",
"Type": "String",
"Default": "###"
},
"ResourceOwner": {
"Description": "tr:resource-owner",
"Type": "String",
"Default": "###"
},
"EnvironmentType": {
"Description": "tr:environment-type",
"Default": "PREPROD",
"Type": "String",
"AllowedValues": ["PREPROD", "PROD"],
"ConstraintDescription": "must specify PREPROD, PROD."
}
},
"Resources": {
"ResourceBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "!Sub a${AssetInsightId}-S3Bucket-${EnvironmentType}",
"VersioningConfiguration": {
"Status": "Enabled"
},
"Tags": [
{
"Key": "tr:application-asset-insight-id",
"Value": "${AssetInsightId}"
},
{
"Key": "tr:resource-owner",
"Value": "${ResourceOwner}"
},
{
"Key": "tr:environment-type",
"Value": "${EnvironmentType}"
}
]
}
}
},
"Outputs": {
"BucketName": {
"Description": "Name of the S3 Resource Bucket",
"Value": "!Ref ResourceBucket"
}
}
}

Using the AWS CLI - for passing dynamic parameters
aws cloudformation create-stack --stack-name my-stack
--template-body template.json
--parameters ParameterKey=EnvironmentType,ParameterValue=Development
From here
Also !Sub is the syntax for the short form of Fn::Sub: - used in template YAML
{ "Fn::Sub" : String } - used in template JSON
Fn::Sub
Change
"BucketName": "!Sub a${AssetInsightId}-S3Bucket-${EnvironmentType}",
To
"BucketName": { "Fn::Sub": "a${AssetInsightId}-S3Bucket-${EnvironmentType}"},
Example

Related

Invalid template resource property "Tags"

For the below cloudformation template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Some Stack",
"Parameters":{
"VpcId":{
"Type": "AWS::EC2::VPC::Id",
"Description": "The target VPC Id"
},
"SubnetId":{
"Type": "AWS::EC2::Subnet::Id",
"Description": "The target subnet Id"
},
"KeyName": {
"Type": "String",
"Description": "The key pair that is allowed SSH access"
}
},
"Resources":{
"EcsCluster":{
"Type": "AWS::ECS::Cluster",
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-ecs-cluster" ] ] }
},
{
"Key": "workenv",
"Value": "dev"
},
{
"Key": "abc",
"Value": "some_value"
},
{
"Key": "function",
"Value": "someapp"
},
{
"Key": "owner",
"Value": "email#abc.com"
}
]
}
},
"Outputs":{
"ElbDomainName":{
"Description": "Public DNS name of Elastic Load Balancer",
"Value": {
"Fn::GetAtt": [
"ElasticLoadBalancer",
"DNSName"
]
}
}
}
}
Below is the error:
Invalid template resource property 'Tags'
Am following the below documentation to add tags:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html
Why CloudFormation service does not accept Tags defined on every resource? something to do with indentation?
It is because the Tags are not defined correctly for EcsCluster resource. The Tags property should be defined inside the Properties section as how you defined Tags for other resources.
"EcsCluster": {
"Type": "AWS::ECS::Cluster",
"Properties": {
"Tags": []
}
}
Hope this helps.
Not all resource types support tags. Check the documentation for each resource type.

Custom geolocation search resolver using aws-amplify api

I'm trying to add a custom geolocation search resolver that targets an Elasticsearch domain using aws-amplify API (base on documentation)
My Custom stack json is :
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "An auto-generated nested stack.",
"Metadata": {},
"Parameters": {
"AppSyncApiId": {
"Type": "String",
"Description": "The id of the AppSync API associated with this project."
},
"AppSyncApiName": {
"Type": "String",
"Description": "The name of the AppSync API",
"Default": "AppSyncSimpleTransform"
},
"env": {
"Type": "String",
"Description": "The environment name. e.g. Dev, Test, or Production",
"Default": "NONE"
},
"S3DeploymentBucket": {
"Type": "String",
"Description": "The S3 bucket containing all deployment assets for the project."
},
"S3DeploymentRootKey": {
"Type": "String",
"Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory."
}
},
"Resources": {
"QueryNearbyUsers": {
"Type": "AWS::AppSync::Resolver",
"Properties": {
"ApiId": {
"Ref": "AppSyncApiId"
},
"DataSourceName": "ElasticsearchDomain",
"TypeName": "Query",
"FieldName": "nearbyUsers",
"RequestMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.nearbyUsers.req.vtl",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
},
"ResponseMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.nearbyUsers.res.vtl",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
}
}
}
},
"Conditions": {
},
"Outputs": {
}
}
But It give me this error :
Resource Name: QueryNearbyUsers (AWS::AppSync::Resolver)
Event Type: create
Reason: No data source found named ElasticsearchDomain (Service: AWSAppSync; Status Code: 404; Error Code: NotFoundException; Request ID: 920993d8-46ef-11e9-82c8-e977f5face03)
I tried many different things for DataSourceName including the domain name in aws console or copy pasting the code from other auto generated stacks,... unfortunately none of them work .
How can I find DataSourceName value?
Seems, there is a typo in their documentation and it should be :
"DataSourceName": "ElasticSearchDomain",
not :
"DataSourceName": "ElasticsearchDomain",
now it's working fine.. so many hours wasted on such a simple typo.

Cloud Formation function doesn't assign value to value for key within a Tag

I have the following Cloud Formation template to create a VPC. The VPC name is generated based off of the region and the environment that the template was created in. The VPC creates without any issues, and running aws cloud formation validate-template --template-url https://foo.template doesn't complaing about any of the syntax.
I would expect the VPC to be named:
vpc-uw1-d-fs
What happens instead is the VPC is left with an empty name and the Name tag has an empty value. Am I not using the function correctly? If I remove the Fn::FindInMap function usage, I get the name generated - it's just missing the environment mapped value.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "VPC for a new environment to use.",
"Parameters": {
"EnvironmentName": {
"Description": "Name of the environment that this VPC will be used for.",
"Type": "String",
"MinLength": "2",
"MaxLength": "20",
"AllowedPattern": "[a-zA-Z]*",
"AllowedValues": [
"Development",
"QA",
"Test",
"Production"
]
}
},
"Resources": {
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsSupport": false,
"EnableDnsHostnames": false,
"InstanceTenancy": "default",
"Tags": [ {
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
"vpc",
{ "Ref": "AWS::Region" },
{ "Fn::FindInMap": [
"EnvironmentMap", { "Ref": "EnvironmentName" }, "AbbreviatedName"
]},
"fs"
]
]
}
}]
}
}
},
"Mappings": {
"RegionMap": {
"us-east-1": {
"regionName": "ue1"
},
"us-west-1": {
"regionName": "uw1"
}
},
"EnvironmentMap": {
"Development": {
"AbbreviatedName": "d"
},
"QA": {
"AbbreviatedName": "qa"
},
"Test": {
"AbbreviatedName": "t"
},
"Production": {
"AbbreviatedName": "p"
}
}
},
"Outputs": {
}
}
Your template is working perfectly fine for me.
I ran it in the ap-southeast-2 region and it produced the tag:
Name: vpc-ap-southeast-2-d-fs
(The RegionMap is not used in the template given.)

Ansible Cloudformation, how to not break if Resource already exists?

I have the following AWS Cloudformation config, which sets up S3, Repositories.
When I run it via an ansible playbook, on the second time running the playbook this happens
AWS::ECR::Repository Repository CREATE_FAILED: production-app-name already exists
etc
How can I make it so that when this is ran multiple times, it will keep the existing s3 and repository instead of just blowing up? (I had assumed the param "DeletionPolicy": "Retain", would do this)
What I'd like to achieve:
If i run this 100x, I want the same resource state as it was after run #1. I do not want any resources deleted/wiped of any data.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Pre-reqs for Elastic Beanstalk application",
"Parameters": {
"BucketName": {
"Type": "String",
"Description": "S3 Bucket name"
},
"RepositoryName": {
"Type": "String",
"Description": "ECR Repository name"
}
},
"Resources": {
"Bucket": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain",
"Properties": {
"BucketName": { "Fn::Join": [ "-", [
{ "Ref": "BucketName" },
{ "Ref": "AWS::Region" }
]]}
}
},
"Repository": {
"Type": "AWS::ECR::Repository",
"DeletionPolicy": "Retain",
"Properties": {
"RepositoryName": { "Ref": "RepositoryName" }
}
}
},
"Outputs": {
"S3Bucket": {
"Description": "Full S3 Bucket name",
"Value": { "Ref": "Bucket" }
},
"Repository": {
"Description": "ECR Repo",
"Value": { "Fn::Join": [ "/", [
{
"Fn::Join": [ ".", [
{ "Ref": "AWS::AccountId" },
"dkr",
"ecr",
{ "Ref": "AWS::Region" },
"amazonaws.com"
]]
},
{ "Ref": "Repository" }
]]}
}
}
}
edit:
DB with similar issue when ran twice
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"DBPassword": {
"MinLength": "8",
"NoEcho": true,
"Type": "String"
},
"Environment": {
"MinLength": "1",
"Type": "String"
},
"DBName": {
"Type": "String",
"Description": "DBName"
},
"DBInstanceIdentifier": {
"Type": "String",
"Description": "DBInstanceIdentifier"
},
"DBPort": {
"Type": "String",
"Description": "DBPort"
},
"DBUsername": {
"Type": "String",
"Description": "DBName"
}
},
"Outputs": {
"Url": {
"Value": {
"Fn::Sub": "postgres://${DBUsername}:${DBPassword}#${Instance.Endpoint.Address}:${Instance.Endpoint.Port}/${DBName}"
}
}
},
"Resources": {
"Instance": {
"Type": "AWS::RDS::DBInstance",
"DeletionPolicy": "Retain",
"Properties": {
"AllocatedStorage": "10",
"DBInstanceClass": "db.t2.micro",
"DBInstanceIdentifier": {"Ref": "DBInstanceIdentifier"},
"DBName": {
"Ref": "DBName"
},
"Engine": "postgres",
"EngineVersion": "9.6.6",
"MasterUsername": {
"Ref": "DBUsername"
},
"MasterUserPassword": {
"Ref": "DBPassword"
},
"MultiAZ": "false",
"Port": {
"Ref": "DBPort"
},
"PubliclyAccessible": "false",
"StorageType": "gp2"
}
}
}
}
The field RepositoryName in AWS::ECR::Repository is actually not required and I would advise against specifying one. By letting CloudFormation dynamically assign a unique name to the repository you'll avoid collision.
If you later want to use the repository name, for exemple: in a task definition, you can use the "Ref" function like so { "Ref": "Repository" } to extract the unique name generated by CloudFormation.
As for the issue with the RDS instance, tt comes down to the same problem of hardcoding resources name.
Using retain will keep the resource alive but it will no longer be managed by CloudFormation which is a big problem.
Just make sure when doing updates to never modify a parameter that require a resource "replacement". The documentation always states what kind of update a parameter change will incur.
Image taken from (here)
If you really need to change a parameter that requires a replacement. Create a new resource with the adapter parameters, migrate whatever data you had in the database or ECR repository, then remove the old resource from the template. If you don't need to migrate anything, make sure you don't have hardcoded names and let CloudFormation perform the replacement.

Template validation error: Template format error: Unrecognized resource types: [AWS::EC2::KeyPair::KeyName]

I am using below could formation template to create a EC2 machine and install elastic search on it using user data.
I have a key pair named "novus1" in my account. When I try creating a stack. I get Unrecognized resource types: [AWS::EC2::KeyPair::KeyName].
Is there any issue in the below JSON template? Any support is appreciated.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Basic template for Novus",
"Resources": {
"novus1": {
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the web server",
"Type": "AWS::EC2::KeyPair::KeyName"
},
"Ec2Instance1": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-4836a428",
"KeyName": "novus1",
"UserData": {
"Fn::Base64": {
"Fn::Join": ["", [
"rpm -ivh elasticsearch-5.2.1.rpm"
]]
}
}
}
}
}
}
The novus1 keyname should be in the Parameters section of your template. You can refer to it using the Ref: object:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Basic template for Novus",
"Parameters": {
"novus1": {
"Type": "AWS::EC2::KeyPair::KeyName",
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the web server"
}
}
"Resources": {
"Ec2Instance1": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-4836a428",
"KeyName": {
"Ref": "novus1"
},
"UserData": {
"Fn::Base64": {
"Fn::Join": ["", [
"rpm -ivh elasticsearch-5.2.1.rpm"
]]
}
}
}
}
}
}
See documentation for more examples on the AWS::EC2::KeyPair::KeyName parameter type.