How we can create Geolocation through AWS:cloudfromation template? - amazon-web-services

I have implemented this but it says invalid request. It may totally wrong.
I want to create geolocation for particular country
"Geolocation": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
"HostedZoneName": {
"Ref": "Route53Domain"
},
"Name": {
"Fn::Join": [
"$",
[
{
"Ref": "javadns"
},
{
"Ref": "Route53Domain"
}
]
]
},
"Type": "CNAME",
"TTL": "60",
"GeoLocation": {"CountryCode" : "*"},
"ResourceRecords": [
{ "Fn::Join" : [".", ["geoip", { "Ref" : "javadns" }]]}
]
}
},

To create a Geo Location record you must specify the SetIdentifier property in CloudFormation.
i see you are missing that property in your CloudFormation and probably that is why it is throwing you an error.
Also here are the code countries identifiers that are supported by Route53, you must not specify the wildcard in that parameter, countries and continent codes consist of two letters.
Documentation here.

Related

Passing values from CloudFormation to Swagger file

I'm creating my infrastructure using CloudFormation.
I'm hoping to create the API using "API Gateway Extensions to OpenAPI"
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html
Relevant code segment looks like this.
"MorningApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Description": {
"Fn::Sub": "${Stage}-MorningApi"
},
"Name": {
"Fn::Sub": "${Stage}-MorningApi"
},
"EndpointConfiguration": {
"Types": [
"REGIONAL"
]
},
"BodyS3Location": {
"Bucket" : "cf-morning",
"Key" : "nested-templates-stack/MorningApiStatusStackSwagger.json"
}
}
},
"MorningApiloadBalancer": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"Name": {
"Fn::Sub": "${Stage}-MorningApiloadBalancer"
},
"Subnets": [
{
"Ref": "PrivateASubnet"
},
{
"Ref": "PrivateBSubnet"
},
{
"Ref": "PrivateCSubnet"
}
],
"Type": {
"Ref": "ELBType"
},
"Scheme": "internal",
"IpAddressType": {
"Ref": "ELBIpAddressType"
}
}
}
I need to pass "DNSName" of the "MorningApiloadBalancer" to the swagger file located in the S3 location. I cannot find a way to do this.
Any help is appreciated.
I haven't tried using swagger with API gateway before.
Using Fn::Transform
Using the Swagger file stored in S3 using the Fn::Tranform macro, it basically takes the content of the swagger file and tranforms into cloudformation.
{
"APIGateway": {
"Type": "AWS::ApiGateway::RestApi",
"Name": "myapi",
"Properties": {
"Body": {
"Fn::Transform": {
"Name": "AWS::Include",
"Parameters": {
"Location": {
"Fn::Sub": "s3://${AWS::AccountId}-swagger-files/${SwaggerFile}"
}
}
}
}
}
}
}
Reference:
https://medium.com/#nabtechblog/integrating-swagger-with-aws-lambda-and-api-gateway-using-cloud-formation-macro-functions-7432dec50dd
Inline swagger definition
I saw an example where the swagger definition is embedded into the cloudformation template. If you do so, you can use intrinsic functions inside the swagger definition. In your case, you can use Ref to get the dns name of the load balancer.
"GreetingApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "Greeting API",
"Description": "API used for Greeting requests",
"FailOnWarnings": true,
"Body": {
"swagger": "2.0",
"paths": {
"/greeting": {
"get": {
"x-amazon-apigateway-integration": {
"uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"GreetingLambda",
"Arn"
]
},
"/invocations"
]
]
}
}
}
}
}
}
}
}
Reference:
https://blog.jayway.com/2016/09/18/introduction-swagger-cloudformation-api-gateway/
Getting DNS Name
I think you know this already.
"Fn::GetAtt" : [ "MorningApiloadBalancer" , "DNSName" ]
Hope this helps.

Outputs are not getting generated based on condition

In my cf template I have set of conditions defined and those conditions are invoked in the resource section as well however when i try to generate outputs using the conditions its not working as expected.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "CloudFormation to Deploy EMR clusters",
"Parameters": {
"Applications": {
"Default": "Core Hadoop",
"Description": "Installed Applications",
"Type": "String",
"AllowedValues" : ["Core Hadoop","HBase"],
"ConstraintDescription": "Must be valid Applications"
},
"awsRegion": {
"Default" : "<my-region>",
"Description": "awsRegion",
"Type": "String"
},
"Ec2KeyName": {
"Default": "<my-key.pem>",
"Description": "Ec2KeyName",
"Type": "String"
}
},
"Resources": {
"EMRCluster1": {
"Type" : "AWS::EMR::Cluster",
"Condition" : "CH",
"Properties" : {
"Applications" : [
{
"Name": "Hadoop"
},
{
"Name": "Hive"
},
{
"Name": "Hue"
},
{
"Name": "Pig"
},
{
"Name": "Mahout"
},
{
"Name": "Tez"
}
]
}
}
"EMRCluster2": {
"Type" : "AWS::EMR::Cluster",
"Condition" : "HB",
"Properties" : {
"Applications": [
{
"Name": "Hadoop"
},
{
"Name": "Hive"
},
{
"Name": "Hue"
},
{
"Name": "HBase"
},
{
"Name": "Phoenix"
},
{
"Name": "ZooKeeper"
}
]
}
}
}
"Conditions" : {
"CH" : {"Fn::Equals" : [{"Ref" : "Applications"}, "Core Hadoop"]},
"HB" : {"Fn::Equals" : [{"Ref" : "Applications"}, "HBase"]}
},
"Outputs": {
"MasterPublicDnsName": {
"Condition" : "CH",
"Value": {
"Fn::GetAtt": [
"EMRCluster1",
"MasterPublicDNS"
]
},
"Description": "MasterPublicDNS for cluster"
},
"MasterPublicDnsName": {
"Condition" : "HB",
"Value": {
"Fn::GetAtt": [
"EMRCluster2",
"MasterPublicDNS"
]
},
"Description": "MasterPublicDNS for cluster"
}
}
Expected: If i choose "CH" in the parameters then it should give cluster1 masterdns and if i choose "HB" it should provide cluster2's masterdns
Actual: If i choose "HB" which is the last section in output, it will give me the masterdns of cluster2 however if i choose "CH" outputs section in cloudformation gives me "No outputs found.".
Can somebody help me on this please.
The problem probably exists because you are using the same name for both outputs. Since the behaviour of JSON with duplicate keys is undefined, the implementing language can choose how to behave in this situation. Presumably, the second time you are using the MasterPublicDnsName as the output name, you are overwriting the first name, which is consistent with the behaviour you are seeing.
You can either opt to use two different names, but this might makes using cross-stack references difficult, or use an Fn::If statement in the value of the output:
"Fn::If": [condition_name, value_if_true, value_if_false]
Or in your case:
"Fn::If": ["CH", {"Fn::GetAtt": ["EMRCluster1", "MasterPublicDNS"]}, {"Fn::GetAtt": ["EMRCluster2", "MasterPublicDNS"]}]
If you need more than two options, you'll need to nest your Fn::If statements

How to refer a derived variable in CloudFormation?

I am looking for small help in CloudFormation and could not find help from documentation or may be not searching the question in right way.
Here is the question.
I am getting availability zones for the environment from mappings as follows.
"AvailabilityZone": {
"Fn::Select": [
"1",
{
"Fn::FindInMap": [
"Environment",
{
"Ref": "EnvType"
},
"AvailabilityZones"
]
}
]
}
I need to use the AZ name in my volume naming convention. How could refer the derived variable "AvailabilityZone" again.?
Currently i am doing this.
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "NamePrefix"
},
{
"Ref": "EnvType"
},
"myconstant",
{
"Fn::Select": [
"2",
{
"Fn::Split": [
"-",
{
"Fn::Select": [
"1",
{
"Fn::FindInMap": [
"Environment",
{
"Ref": "EnvType"
},
"AvailabilityZones"
]
}
]
}
]
}
]
}
]
]
}
}
I am doing the same code twice.
How can i re-use the derived variable here.?
Unfortunately, the short answer is you can't. Hopefully someday AWS supports variables in CloudFormation.
There are some hacks that might be of interest. Emphasis on hack!
Use a CloudFormation pre-processor that does what you want (eg, Troposphere)
Use a Custom Resource that outputs the value, then use GetAtt to refer to the value. (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html)
Use the AWS::Include transform to refer to a template snippet in S3 (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html), in that case you're not really using variables, off course.

Can't send parameters from a child CloudFormation template to another child template

I am crafting a monstrosity of a CloudFormation template that uses the AWS::CloudFormation::Stack resource to spawn other nested stacks. The purpose of this is the classic separation of duties and management.
The overall goal is to build an application environment from the ground up (starting with VPC and ending with the application instances).
So far, most of it works. But I've hit a road block with referencing the outputs of the subnet CF template and the securitygroup CF template for use in creating the EC2 instances.
It works like this:
Master template --> Builds VPC --> Calls child template to build subnets --> Calls child template to build security groups --> Calls child template to build EC2 instances
I need to pass the outputs from the subnet and security group templates to the EC2 instance template so the instances can be provisioned into the correct part of the architecture. The VPC ID ref and KeyPairs pass in fine, but the subnetID and securitygroupID do not.
Here's the portion of the master template that calls the security group/subnet templates:
"DevNetworkStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": [
"SNVPC",
"SNIGW"
],
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevNetworkStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"VPCID": {
"Ref": "SNVPC"
},
"SNIGW": {
"Ref": "SNIGW"
}
}
}
},
"DevSecurityGroupsStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "DevNetworkStack",
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevSecurityGroupsStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"VPCID": {
"Ref": "SNVPC"
}
}
}
},
These work fine. They create and everything is fine. The templates offer outputs like so:
"Outputs": {
"DevAdminSubnetID": {
"Description": "DevAdminSubnetID",
"Value": {
"Ref": "DevAdminSubnet"
}
},
...
"Outputs": {
"DevAdminSecurityGroupID": {
"Description": "DevAdminSecurityGroupID",
"Value": {
"Ref": "DevAdminSecurityGroup"
}
},
I can see the outputs in the CF console.
Now, the next template is trying to use the Security Group ID and the Subnet ID. But it's not working.
Master template calls the next child as:
"DevAdminStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": [
"DevNetworkStack",
"DevSecurityGroupsStack",
"EC2DevRoleInstanceProfile",
"S3DevUserDataBucket",
"S3DevHomeDirsDataBucket"
],
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevAdminStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"AdminKeyPair": {
"Ref": "AdminServersKeyPair"
},
"VPCID": {
"Ref": "SNVPC"
},
"DevAdminSecurityGroupID": [
{
"Fn::GetAtt": [
"DevSecurityGroupsStack",
"Outputs.DevAdminSecurityGroupID"
]
}
],
"DevAdminSubnetID": [
{
"Fn::GetAtt": [
"DevNetworkStack",
"Outputs.DevAdminSubnetID"
]
}
]
}
}
}
...and the child template looks like this (removed some portions for brevity because I'm just testing right now)
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Dev-Admin",
"Mappings": {
"RegionMap": {
"DevAdminServer": {
"AMI": "ami-6fc9770e",
"InstanceType": "t2.micro"
}
}
},
"Parameters": {
"AdminKeyPair": {
"Type": "AWS::EC2::KeyPair::KeyName"
},
"VPCID": {
"Type": "AWS::EC2::VPC::Id"
},
"DevAdminSecurityGroupID": {
"Type": "AWS::EC2::SecurityGroup::Id"
},
"DevAdminSubnetID": {
"Type": "AWS::EC2::Subnet::Id"
}
},
"Resources": {
"DevAdminServer": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"Comment": "Sets up administrative tools for the server",
"AWS::CloudFormation::Init": {
"config": {
"packages": {
"yum": {}
},
"files": {},
"services": {}
}
}
},
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"RegionMap",
"DevAdminServer",
"AMI"
]
},
"SecurityGroupIds": [
{
"Ref": "DevAdminSecurityGroupID"
}
],
"SubnetId": {
"Ref": "DevAdminSubnetID"
},
"InstanceType": {
"Fn::FindInMap": [
"RegionMap",
"DevAdminServer",
"InstanceType"
]
},
"KeyName": {
"Ref": "AdminKeyPair"
},
"Tags": [
{
"Key": "Application",
"Value": {
"Ref": "AWS::StackId"
}
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[]
]
}
}
}
}
}
}
But this resource fails on creation with the error:
Value of property Parameters must be an object with String (or simple type) properties
I know it's the last two variables causing the trouble (subnetID and securitygroupID), because I removed them and the provisioning the child template works just fine.
What am I doing wrong?
The values of DevAdminSecurityGroupID and DevAdminSubnetID are defined as JSON arrays [] in your master template, but they should be Strings (after the Fn::GetAtt intrinsic function expansion):
"DevAdminSecurityGroupID":
{
"Fn::GetAtt": [
"DevSecurityGroupsStack",
"Outputs.DevAdminSecurityGroupID"
]
},
"DevAdminSubnetID":
{
"Fn::GetAtt": [
"DevNetworkStack",
"Outputs.DevAdminSubnetID"
]
}

Elasticache replication group id in CloudFormation template

How do you set the Redis ReplicationGroup ID when using a CloudFormation template? All the options in the docs show no way to do that and the CLI you can do this easily. My end goal is to have a Redis replication group with 3 cluster members but I want to choose the name rather than AWS set a unique name for me.
Here's a snippet of my template:
"Resources": {
"mqpReplicationGroup": {
"Type": "AWS::ElastiCache::ReplicationGroup",
"Properties": {
"CacheNodeType": {
"Ref": "CacheNodeType"
},
"CacheSubnetGroupName": {
"Ref": "CacheSubnets"
},
"ReplicationGroupDescription": "Redis Replication Group",
"Engine": "redis",
"EngineVersion": {
"Ref": "RedisVersion"
},
"NumCacheClusters": {
"Ref": "NumberOfCacheNodes"
},
"AutoMinorVersionUpgrade": "true",
"AutomaticFailoverEnabled": "true",
"PreferredMaintenanceWindow": "sat:09:25-sat:22:30",
"SnapshotRetentionLimit": "4",
"SnapshotWindow": "00:05-05:30",
"NotificationTopicArn": {
"Fn::Join" :[":",["arn:aws:sns",{ "Ref" : "AWS::Region" },{ "Ref" : "AWS::AccountId" },"service-aws"]]
},
"SecurityGroupIds": [
{
"Fn::Join": [
",",
{
"Ref": "VpcSecurityGroupIds"
}
]
}
]
}
},
"CacheSubnets": {
"Type": "AWS::ElastiCache::SubnetGroup",
"Properties": {
"Description": "mqp-cache-subnet",
"SubnetIds": {
"Ref": "SubnetIds"
}
}
}
}
As of 2017, this is now possible using ReplicationGroupId property.
Since it is optional, AWS CloudFormation will still generate an unique physical ID when not specified.
Restrictions for Name Type:
Must contain from 1 to 20 alphanumeric characters or hyphens.
First character must be a letter.
Cannot end with a hyphen or contain two consecutive hyphens.
This cannot currently be done with CloudFormation.