HoztedZone error on cloudformation - amazon-web-services

I´m using cloudformation to create a ecs container and add this new container into route53 hostzone.
But when I run this script I´m having problems with the HostedZone tags
Here The error
A client error (ValidationError) occurred when calling the CreateStack operation: Invalid template parameter property 'Properties'
Here the json
"Parameters" : {
"InstanceType" : {
"Description" : "Container Instance type",
"Type" : "String",
"Default" : "t2.medium",
"AllowedValues" : [ "t2.micro", "t2.small", "t2.medium", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge" ],
"ConstraintDescription" : "must be a valid EC2 instance type."
},
"HostedZone" : {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"HostedZoneConfig": {
"Comment": "My hosted zone for example.com"
},
"Name": "***.couchbase.com",
"VPCs": [
{
"VPCId": "*********",
"VPCRegion": "eu-west-1"
}
],
"HostedZoneTags": [
{
"Key": "Name",
"Value": "Couchbase DNS"
}
]
}
}
},
"Resources" : {
"ContainerInstance" : {
"Type": "AWS::EC2::Instance",
"Properties": {
"Tags": [{
"Key" : "Name",
"Value" : "Couchbase-1"
},
{
"Key" : "Type",
"Value" : "ECS-Couchbase"
}],
"IamInstanceProfile" : { "Ref" : "ECSIamInstanceProfile" },
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"InstanceType" : { "Ref" : "InstanceType" },
"SecurityGroups" : [ "ssh","default", "couchbase" ],
"KeyName" : { "Ref" : "KeyName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -xe\n",
"echo ECS_CLUSTER=", { "Ref" : "ClusterName" },
" >> /etc/ecs/ecs.config\n"
]]}}
}
},
"CouchbaseDNSRecord" : {
"Type" : "AWS::Route53::RecordSet",
"Properties" : {
"HostedZoneName" : {
"Fn::Join" : [ "", [
{ "Ref" : "HostedZone" }, "."
] ]
},
"Comment" : "DNS name for my instance.",
"Name" : {
"Fn::Join" : [ "", [
{"Ref" : "ContainerInstance"}, ".",
{"Ref" : "AWS::Region"}, ".",
{"Ref" : "HostedZone"} ,"."
] ]
},
"Type" : "A",
"TTL" : "900",
"ResourceRecords" : [
{ "Fn::GetAtt" : [ "ContainerInstance", "PublicIp" ] }
]
}
},

The HostedZone should be inside the Resources section.
"Parameters" : {
"InstanceType" : {
...
}
},
"Resources" : {
"HostedZone" : {
...
},
"ContainerInstance" : {
...
},
...
}

All the resources you want to create using Cloudformation should be within the resources section. This gives a better anatomy of the template, http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html

Related

aws cfn-init failing for AccessDenied

I have to run cfn-init for an autoscaling group but it is failing:
/opt/aws/bin/cfn-init -v --stack ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU --resource LaunchConfig --region us-west-2
AccessDenied: Instance i-02c0239a16cd96a53 is not allowed to call DescribeStackResource for ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU
The policy is on * and, indeed, i can call describe-stack-resource on the same entity and it is working fine:
[root#ip-172-31-18-99 ec2-user]# aws cloudformation describe-stack-resource --stack-name ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU --logical-resource-id LaunchConfig --region us-west-2
{
"StackResourceDetail": {
"StackId": "arn:aws:cloudformation:us-west-2:020779576776:stack/ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU/cdf015d0-9d88-11ed-8084-0af2362739b7",
"ResourceStatus": "CREATE_COMPLETE",
"DriftInformation": {
"StackResourceDriftStatus": "NOT_CHECKED"
},
"ResourceType": "AWS::AutoScaling::LaunchConfiguration",
"LastUpdatedTimestamp": "2023-01-26T14:53:46.144Z",
"StackName": "ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU",
"PhysicalResourceId": "ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU-LaunchConfig-IqXSDhGUFDY1",
"Metadata": "{\"Comment\":\"Install a simple application\",\"AWS::CloudFormation::Init\":{\"config\":{\"files\":{\"/etc/cfn/cfn-hup.conf\":{\"mode\":\"000400\",\"owner\":\"root\",\"content\":\"[main]\\nstack=arn:aws:cloudformation:us-west-2:020779576776:stack/ms-lbas-split1/86b538d0-9d88-11ed-8250-0a502f832635\\nregion=us-west-2\\n\",\"group\":\"root\"},\"/etc/cfn/hooks.d/cfn-auto-reloader.conf\":{\"mode\":\"000400\",\"owner\":\"root\",\"content\":\"[cfn-auto-reloader-hook]\\ntriggers=post.update\\npath=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\\naction=/opt/aws/bin/cfn-init -v --stack ms-lbas-split1-EC2LauncConfigStack-1GB6170KAE3EU --resource LaunchConfig --region us-west-2\\nrunas=root\\n\",\"group\":\"root\"},\"/var/www/html/index.html\":{\"mode\":\"000644\",\"owner\":\"root\",\"content\":\"<img src=\\\"\\nhttps://s3-us-west-2.amazonaws.com/cloudformation-examples-us-west-2\\n/cloudformation_graphic.png\\\" alt=\\\"AWS CloudFormation Logo\\\"/>\\n<h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>\",\"group\":\"root\"}},\"services\":{\"sysvinit\":{\"cfn-hup\":{\"files\":[\"/etc/cfn/cfn-hup.conf\",\"/etc/cfn/hooks.d/cfn-auto-reloader.conf\"],\"ensureRunning\":\"true\",\"enabled\":\"true\"},\"httpd\":{\"ensureRunning\":\"true\",\"enabled\":\"true\"}}},\"packages\":{\"yum\":{\"httpd\":[]}}}}}",
"LogicalResourceId": "LaunchConfig"
}
I have this problem only when I am using those nested cloudformation template:
Main one:
"Resources": {
"WebServerGroup" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"VPCZoneIdentifier" : { "Ref" : "AWSsubnetid" },
"LaunchConfigurationName" : {
"Fn::GetAtt": ["EC2LauncConfigStack", "Outputs.LaunchConfig"]
},
"MinSize" : "2",
"MaxSize" : "2",
"TargetGroupARNs" : [ { "Ref" : "ALBTargetGroup" } ]
},
"CreationPolicy" : {
"ResourceSignal" : {
"Timeout" : "PT60M"
}
},
"UpdatePolicy": {
"AutoScalingRollingUpdate": {
"MinInstancesInService": "1",
"MaxBatchSize": "1",
"PauseTime" : "PT15M",
"WaitOnResourceSignals": "true"
}
}
},
"EC2LauncConfigStack": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": {
"Fn::Sub": "https://ms-tst-launcongi-12343.s3.us-west-2.amazonaws.com/ec2_EC2LauncConfigStack-v2.json"
},
"Parameters":{
"StackName": {
"Ref" : "AWS::StackName"
},
"StackId": {
"Ref" : "AWS::StackId"
},
"InstanceSecGroup" : {
"Ref" : "InstanceSecurityGroup"
}
}
}
},
"ApplicationLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancingV2::LoadBalancer",
"DependsOn":"InstanceSecurityGroupHttp",
"Properties" : {
"Subnets" : { "Ref" : "AWSsubnetid"},
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroupHttp" } ]
}
},
"ALBListener" : {
"Type" : "AWS::ElasticLoadBalancingV2::Listener",
"Properties" : {
"DefaultActions" : [{
"Type" : "forward",
"TargetGroupArn" : { "Ref" : "ALBTargetGroup" }
}],
"LoadBalancerArn" : { "Ref" : "ApplicationLoadBalancer" },
"Port" : "80",
"Protocol" : "HTTP"
}
},
"ALBTargetGroup" : {
"Type" : "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties" : {
"HealthCheckIntervalSeconds" : 30,
"HealthCheckTimeoutSeconds" : 5,
"HealthyThresholdCount" : 3,
"Port" : 80,
"Protocol" : "HTTP",
"UnhealthyThresholdCount" : 5,
"VpcId" : {"Ref" : "AWSvpcid"}
}
},
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable SSH access and HTTP access on the inbound port",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "80",
"ToPort" : "80",
"SourceSecurityGroupId" : {"Fn::Select" : [0, {"Fn::GetAtt" : ["ApplicationLoadBalancer", "SecurityGroups"]}]}
},{
"IpProtocol" : "tcp",
"FromPort" : "22",
"ToPort" : "22",
"CidrIp" : { "Ref" : "RemoteAccessCIDR"}
} ],
"VpcId" : { "Ref" : "AWSvpcid" }
}
},
"InstanceSecurityGroupHttp" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable HTTP access on the inbound port",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "80",
"ToPort" : "80",
"CidrIp": "0.0.0.0/0"
} ],
"VpcId" : { "Ref" : "AWSvpcid" }
}
}
},
Nested:
[...]
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"*"
],
"Resource": "*"
},
[...]
"LaunchConfig" : {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Metadata" : {
"Comment" : "Install a simple application",
"AWS::CloudFormation::Init" : {
"config" : {
"packages" : {
"yum" : {
"httpd" : []
}
},
"files" : {
"/var/www/html/index.html" : {
"content" : { "Fn::Join" : ["\n", [
"<img src=\"", {"Fn::FindInMap" : ["Region2Examples", {"Ref" : "AWS::Region"}, "Examples"]}, "/cloudformation_graphic.png\" alt=\"AWS CloudFormation Logo\"/>",
"<h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>"
]]},
"mode" : "000644",
"owner" : "root",
"group" : "root"
},
"/etc/cfn/cfn-hup.conf" : {
"content" : { "Fn::Join" : ["", [
"[main]\n",
"stack=", { "Ref" : "StackId" }, "\n",
"region=", { "Ref" : "AWS::Region" }, "\n"
]]},
"mode" : "000400",
"owner" : "root",
"group" : "root"
},
"/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
"content": { "Fn::Join" : ["", [
"[cfn-auto-reloader-hook]\n",
"triggers=post.update\n",
"path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\n",
"action=/opt/aws/bin/cfn-init -v ",
" --stack ", { "Ref" : "StackName" },
" --resource LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, "\n",
"runas=root\n"
]]},
"mode" : "000400",
"owner" : "root",
"group" : "root"
}
},
"services" : {
"sysvinit" : {
"httpd" : { "enabled" : "true", "ensureRunning" : "true" },
"cfn-hup" : { "enabled" : "true", "ensureRunning" : "true",
"files" : ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"]}
}
}
}
}
},
"Properties" : {
"ImageId" :{ "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "AWSInstanceType" }, "Arch" ] } ] },
"SecurityGroups" : [ { "Ref" : "InstanceSecGroup" } ],
"IamInstanceProfile": { "Ref": "HostProfile" },
"InstanceType" : { "Ref" : "AWSInstanceType" },
"KeyName" : { "Ref" : "AWSKeyPairName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -xe\n",
"yum update -y aws-cfn-bootstrap\n",
"/opt/aws/bin/cfn-init -v ",
" --stack ", { "Ref" : "AWS::StackName" },
" --resource LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, "\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ", { "Ref" : "StackName" },
" --resource WebServerGroup ",
" --region ", { "Ref" : "AWS::Region" }, "\n"
]]}}
}
}
},
I have chopped out few things. Exactly the same code is running fine when I am using one single stack:
{
"Resources": {
"WebServerGroup" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"VPCZoneIdentifier" : { "Ref" : "AWSsubnetid" },
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : "2",
"MaxSize" : "2",
"TargetGroupARNs" : [ { "Ref" : "ALBTargetGroup" } ]
},
"CreationPolicy" : {
"ResourceSignal" : {
"Timeout" : "PT15M"
}
},
"UpdatePolicy": {
"AutoScalingRollingUpdate": {
"MinInstancesInService": "1",
"MaxBatchSize": "1",
"PauseTime" : "PT15M",
"WaitOnResourceSignals": "true"
}
}
},
"LaunchConfig" : {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Metadata" : {
"Comment" : "Install a simple application",
"AWS::CloudFormation::Init" : {
"config" : {
"packages" : {
"yum" : {
"httpd" : []
}
},
"files" : {
"/var/www/html/index.html" : {
"content" : { "Fn::Join" : ["\n", [
"<img src=\"", {"Fn::FindInMap" : ["Region2Examples", {"Ref" : "AWS::Region"}, "Examples"]}, "/cloudformation_graphic.png\" alt=\"AWS CloudFormation Logo\"/>",
"<h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>"
]]},
"mode" : "000644",
"owner" : "root",
"group" : "root"
},
"/etc/cfn/cfn-hup.conf" : {
"content" : { "Fn::Join" : ["", [
"[main]\n",
"stack=", { "Ref" : "AWS::StackId" }, "\n",
"region=", { "Ref" : "AWS::Region" }, "\n"
]]},
"mode" : "000400",
"owner" : "root",
"group" : "root"
},
"/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
"content": { "Fn::Join" : ["", [
"[cfn-auto-reloader-hook]\n",
"triggers=post.update\n",
"path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\n",
"action=/opt/aws/bin/cfn-init -v ",
" --stack ", { "Ref" : "AWS::StackName" },
" --resource LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, "\n",
"runas=root\n"
]]},
"mode" : "000400",
"owner" : "root",
"group" : "root"
}
},
"services" : {
"sysvinit" : {
"httpd" : { "enabled" : "true", "ensureRunning" : "true" },
"cfn-hup" : { "enabled" : "true", "ensureRunning" : "true",
"files" : ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"]}
}
}
}
}
},
"Properties" : {
"ImageId" :{ "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "AWSInstanceType" }, "Arch" ] } ] },
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"InstanceType" : { "Ref" : "AWSInstanceType" },
"KeyName" : { "Ref" : "AWSKeyPairName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -xe\n",
"yum update -y aws-cfn-bootstrap\n",
"/opt/aws/bin/cfn-init -v ",
" --stack ", { "Ref" : "AWS::StackName" },
" --resource LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, "\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ", { "Ref" : "AWS::StackName" },
" --resource WebServerGroup ",
" --region ", { "Ref" : "AWS::Region" }, "\n"
]]}}
}
},
"ApplicationLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties" : {
"Subnets" : { "Ref" : "AWSsubnetid"},
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroupHttp" } ]
}
},
"ALBListener" : {
"Type" : "AWS::ElasticLoadBalancingV2::Listener",
"Properties" : {
"DefaultActions" : [{
"Type" : "forward",
"TargetGroupArn" : { "Ref" : "ALBTargetGroup" }
}],
"LoadBalancerArn" : { "Ref" : "ApplicationLoadBalancer" },
"Port" : "80",
"Protocol" : "HTTP"
}
},
"ALBTargetGroup" : {
"Type" : "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties" : {
"HealthCheckIntervalSeconds" : 30,
"HealthCheckTimeoutSeconds" : 5,
"HealthyThresholdCount" : 3,
"Port" : 80,
"Protocol" : "HTTP",
"UnhealthyThresholdCount" : 5,
"VpcId" : {"Ref" : "AWSvpcid"}
}
},
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable SSH access and HTTP access on the inbound port",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "80",
"ToPort" : "80",
"SourceSecurityGroupId" : {"Fn::Select" : [0, {"Fn::GetAtt" : ["ApplicationLoadBalancer", "SecurityGroups"]}]}
},{
"IpProtocol" : "tcp",
"FromPort" : "22",
"ToPort" : "22",
"CidrIp" : { "Ref" : "RemoteAccessCIDR"}
} ],
"VpcId" : { "Ref" : "AWSvpcid" }
}
},
"InstanceSecurityGroupHttp" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable HTTP access on the inbound port",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "80",
"ToPort" : "80",
"CidrIp": "0.0.0.0/0"
} ],
"VpcId" : { "Ref" : "AWSvpcid" }
}
}
},
"Outputs" : {
"URL" : {
"Description" : "URL of the website",
"Value" : { "Fn::Join" : [ "", [ "http://", { "Fn::GetAtt" : [ "ApplicationLoadBalancer", "DNSName" ]}]]}
}
}
}

Retain Output on Stack-Update

I wrote a Cloudformation template that creates a stack with a DynamoDB Table:
"FeedStageDynamoTable" : {
"Type" : "AWS::DynamoDB::Table",
"UpdateReplacePolicy" : "Retain",
"Properties" : {
"TableName" : { "Fn::Sub": [ "feed-${Year}-${Environment}-table", { "Year": {"Ref" : "BundleYear" }, "Environment" : {"Ref" : "DeployEnvironment"}} ]},
"AttributeDefinitions" : [
{"AttributeName" : "Guid", "AttributeType" : "S"}
],
"KeySchema" : [
{"AttributeName" : "Guid", "KeyType" : "HASH"}
],
"ProvisionedThroughput" : {
"ReadCapacityUnits" : "2",
"WriteCapacityUnits" : "2"
},
"StreamSpecification": {
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
}
and an Output for the Table's Stream:
"Outputs" : {
"FeedStageTableStreamArn": {
"Description" : "The security group ID to use for public web servers",
"Value" : { "Fn::GetAtt" : ["FeedStageDynamoTable", "StreamArn"] },
"Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-FeedStageDynamoTableStreamArn" }}
},
The output is used by a lambda function from another template (for the second stack):
"NotifyWebConsumer" : {
"Type" : "AWS::Serverless::Function",
"Properties": {
"Environment": {
"Variables" : {
"EnvironmentCodename" : { "Fn::Sub": [ "${Environment}", { "Environment" : {"Ref" : "DeployEnvironment"}} ]}
}
},
"Handler": "AmazonServerlessStageWebUpdate::AmazonServerlessStageWebUpdate.Functions::NotifyWebConsumer",
"FunctionName": "NotifyWebConsumer",
"Runtime": "dotnetcore2.1",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Policies":[{ "Fn::ImportValue" : {"Fn::Sub" : "${DeployEnvironment}-LambdaExecutionPolicy"} }],
"Role":{"Fn::GetAtt": ["NotifyConsumerRole","Arn"]},
"Events": {
}
}
}
...
"EventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"Properties": {
"EventSourceArn": { "Fn::ImportValue" : {"Fn::Sub" : "StageEnvironment-FeedStageDynamoTableStreamArn"} },
"FunctionName" : {
"Fn::GetAtt": [
"NotifyWebConsumer", "Arn"
]
},
"StartingPosition" : "LATEST"
}
}
After I published both stacks I can not update the first one anymore, because the output stream is in use by the second stack. Now my question: is there a cloudformation property like "UpdateReplacePolicy": "Retain" for outputs? Or is there any other way to update the first stack without deleting the second one?

Target group health check for ECS service failing upon creation with cloudformation

I've been hosting an api as a service on ecs with an alb. Whenver the service is running it fails the health check and continually kills and restarts the task. The task logs always show it repsonding to the health checks with response 200 so it should be passing. I've checked that my security groups and vpc's are configured correctly. I will post my cloud formation template below in hopes someone can find my mistake. I've also tried increasing the health check graces period greatly and it hasn't helped the issue.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata": {
"AWS::CloudFormation::Designer": {
"6514f997-7e0a-48c6-9e5f-95465c35ff00": {
"size": {
"width": 60,
"height": 60
},
"position": {
"x": 280,
"y": 140
},
"z": 0,
"embeds": []
}
}
},
"Parameters": {
"ClusterName": {
"Type" : "String",
"Description" : "Name of the ECS Cluster",
"Default" : "Rest-Api-Explorer"
},
"Subnet": {
"Description": "User Specified Subnet",
"Type": "AWS::EC2::Subnet::Id",
"ConstraintDescription": "Must be a valid Subnet of specified VPC."
}
},
"Mappings" : {
"SubnetToVPCMap" : {
"subnet-58863c01" : {"VPC" : "vpc-6581f100"},
"subnet-ae349ec6" : {"VPC" : "vpc-259a344d"},
"subnet-b9349ed1" : {"VPC" : "vpc-259a344d"},
"subnet-d8349eb0" : {"VPC" : "vpc-259a344d"},
"subnet-e9b7d69e" : {"VPC" : "vpc-6581f100"},
"subnet-ee97d48b" : {"VPC" : "vpc-6581f100"}
},
"VPCToSecurityGroupMap" : {
"vpc-6581f100" : {"SecurityGroup" : "sg-a5c672c1"},
"vpc-259a344d" : {"SecurityGroup" : "sg-1d658072"}
},
"VPCToTag" : {
"vpc-6581f100" : {"Tag" : [{ "Key": "Name", "Value": "Rest API Explorer"}, {"Key": "Type", "Value": "Development"}]},
"vpc-259a344d" : {"Tag" : [{ "Key": "Name", "Value": "Rest API Explorer"}, {"Key": "Type", "Value": "Production"}]}
}
},
"Resources": {
"ECSCluster":{
"Type" : "AWS::ECS::Cluster",
"Properties" : {
"ClusterName" : {"Ref" : "ClusterName"},
"Tags": {
"Fn::FindInMap" : [ "VPCToTag", {"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]} , "Tag"]
}
}
},
"EC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"IamInstanceProfile": "ecsInstanceRole",
"ImageId": "ami-00e0090ac21971297",
"InstanceType": "t2.medium",
"KeyName": "community-admin",
"SecurityGroupIds": [
{"Fn::FindInMap" : [ "VPCToSecurityGroupMap", {"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]} , "SecurityGroup"]}
],
"SubnetId": {
"Ref": "Subnet"
},
"Tags": {
"Fn::FindInMap" : [ "VPCToTag", {"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]} , "Tag"]
},
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"echo ECS_CLUSTER=", { "Ref" : "ClusterName" },
" >> /etc/ecs/ecs.config\n"
]
]
}
}
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "6514f997-7e0a-48c6-9e5f-95465c35ff00"
}
}
},
"ECSALB":{
"Type":"AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties":{
"Name":"rest-api-explorer-2",
"Scheme":"internal",
"LoadBalancerAttributes": [
{
"Key":"idle_timeout.timeout_seconds",
"Value":"60"
}
],
"SecurityGroups":[
{
"Fn::FindInMap" : [ "VPCToSecurityGroupMap", {"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]} , "SecurityGroup"]
}
],
"Subnets" : [
"subnet-58863c01",
"subnet-e9b7d69e",
"subnet-ee97d48b"
]
}
},
"ALBListener":{
"Type":"AWS::ElasticLoadBalancingV2::Listener",
"DependsOn": "ECSTG",
"Properties":{
"DefaultActions":[
{
"Type":"forward",
"TargetGroupArn":{
"Ref":"ECSTG"
}
}
],
"LoadBalancerArn":{
"Ref":"ECSALB"
},
"Port":"80",
"Protocol":"HTTP"
}
},
"ECSTG":{
"Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
"DependsOn":"ECSALB",
"Properties":{
"HealthCheckIntervalSeconds":15,
"HealthCheckPath":"/api/rest/console",
"HealthCheckPort":"traffic-port",
"HealthCheckProtocol":"HTTP",
"HealthCheckTimeoutSeconds":10,
"HealthyThresholdCount":3,
"Name":"rest-api-target-group-2",
"Port":3000,
"Protocol":"HTTP",
"TargetType":"ip",
"UnhealthyThresholdCount":5,
"VpcId":{
"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]
}
}
},
"service":{
"Type":"AWS::ECS::Service",
"DependsOn": "ALBListener",
"Properties":{
"Cluster":{
"Ref":"ECSCluster"
},
"DeploymentConfiguration": {
"MaximumPercent": 100,
"MinimumHealthyPercent": 0
},
"DesiredCount":"1",
"HealthCheckGracePeriodSeconds" : 90,
"LaunchType": "FARGATE",
"LoadBalancers":[
{
"ContainerName":"api-rest-explorer",
"ContainerPort":"3000",
"TargetGroupArn":{
"Ref":"ECSTG"
}
}
],
"NetworkConfiguration": {
"AwsvpcConfiguration" : {
"AssignPublicIp" : "ENABLED",
"SecurityGroups" : [
{
"Fn::FindInMap" : [ "VPCToSecurityGroupMap", {"Fn::FindInMap" : [ "SubnetToVPCMap", {"Ref" : "Subnet"}, "VPC"]} , "SecurityGroup"]
}
],
"Subnets" : [
{"Ref": "Subnet"}
]
}
},
"SchedulingStrategy": "REPLICA",
"ServiceName" : "rest-api-explorer-fargate",
"TaskDefinition": "rest-api-explorer-fargate:4"
}
}
}
}

AWS CloudFormation identify IP of another instance - circular dependency

I'm provisioning two instances with CloudFormation template. "MASTER" and "SLAVE".
At the userdata script I need to pass slave's private IP to master and master's IP to slave.
Here is my template:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "",
"Parameters" : {
},
"Resources" : {
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Default Ports",
"SecurityGroupIngress" : [
{ "IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0"}
]
}
},
"MASTER" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"Tags":[{"Key":"Name", "Value":"MASTER"}],
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=MASTER SLAVEIP=",?????," sh bootstrap.sh","\n"
] ] } }
}
},
"SLAVE" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"Tags":[{"Key":"Name", "Value":"SLAVE"}],
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=SLAVE MASTERIP=",?????," sh bootstrap.sh","\n"
] ] } }
}
},
},
"Outputs" : {
}
}
What would be right replacement for ????? if it's possible at all and if not - what alternative can I use?
UPD: found this: {"Fn::GetAtt": ["MASTER","PrivateIp"]}, it works fine on it's own, but fails with "Template validation error: Circular dependency between resources: [SLAVE, MASTER]" if I'm trying to do both master and slave IPs.
If you're using VPCs and Subnets, you can do this by creating an AWS::EC2::NetworkInterface for each instance. Then use { "Fn::GetAtt": [ "MyNetworkInterface", "PrimaryPrivateIpAddress" ] } in user data to reference the internal IP address of the network interface
You associate a network interface with an EC2 instance using the NetworkInterfaces property
...
"MasterNetInt" : {
"Type" : "AWS::EC2::NetworkInterface",
"Properties" : {
"SubnetId": { "Ref" : "MySubnet" }
}
},
"SlaveNetInt" : {
"Type" : "AWS::EC2::NetworkInterface",
"Properties" : {
"SubnetId": { "Ref" : "MySubnet" }
}
},
"Master" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"SubnetId": { "Ref" : "MySubnet" },
"NetworkInterfaces": [ { "Ref" : "MasterNetInt" } ],
"UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=MASTER SLAVEIP=", { "Fn::GetAtt": [ "SlaveNetInt", "PrimaryPrivateIpAddress" ] }," sh bootstrap.sh","\n"
] ] } }
}
},
"Slave" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"SubnetId": { "Ref" : "MySubnet" },
"NetworkInterfaces": [ { "Ref" : "SlaveNetInt" } ],
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=SLAVE MASTERIP=", { "Fn::GetAtt": [ "MasterNetInt", "PrimaryPrivateIpAddress" ] }," sh bootstrap.sh","\n"
] ] } }
}
}
...
If you're unfamiliar with setting up a VPC and Subnets, read the following documentation: http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario1.html
and reference these template examples: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/sample-templates-services-us-west-2.html#d0e113371
The basic requirements are:
AWS::EC2::VPC
AWS::EC2::InternetGateway
AWS::EC2::VPCGatewayAttachment
AWS::EC2::RouteTable
AWS::EC2::Route
AWS::EC2::Subnet
AWS::EC2::SubnetRouteTableAssociation
AWS::EC2::NetworkAcl
AWS::EC2::SubnetNetworkAclAssociation
AWS::EC2::NetworkAclEntry
AWS::EC2::NetworkInterface
AWS::EC2::Instance

How are my ENIs being created?

I have the below code but I don't see where an actual ENI is being created "AWS::EC2::NetworkInterface". I assume this is being done within the UserData section of the script after the "UTMLaunchConfiguration" runs. I'm still learning so I'm sure this isn't a terribly difficult config but I'm still learning how to read it.
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Sophos UTM HA template",
"Mappings" : {
"RegionMap" : {
"us-gov-west-1" : { "BYOL" : "ami-09f7972a", "AZ1" : "us-gov-west-1a", "AZ2" : "us-gov-west-1b", "EC2API" : "ec2.us-gov-west-1.amazonaws.com" }
}
},
"Parameters" : {
"01LicenseType": {
"Description": "Hourly or BYOL",
"Type" : "String",
"Default" : "Hourly",
"AllowedValues" : [ "Hourly", "BYOL" ]
},
"02InstanceType" : {
"Description" : "Amazon EC2 instance type",
"Type" : "String",
"Default" : "m3.medium",
"AllowedValues" : [ "t1.micro", "m1.small", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge"]
,
"ConstraintDescription" : "must be a valid EC2 instance type."
},
"03Hostname" : {
"Description" : "The hostname field can have a maximum of 64 characters. Hostname can only contain lower and uppercase letters, numbers, dots(.), dashes(-) and needs to begin with a letter or a number.",
"Type" : "String",
"MaxLength" : "64",
"AllowedPattern" : "^[0-9A-Za-z]{1}([A-Za-z0-9.-]*)$",
"ConstraintDescription" : "Invalid hostname. The hostname field can have a maximum of 64 characters. Hostname can only contain lower- and uppercase letters, numbers, dots(.), dashes(-) and needs to begin with a letter or a number."
},
"04City" : {
"Description" : "City",
"Type" : "String"
},
"05Country" : {
"Description" : "Country",
"Type" : "String",
"AllowedValues" : ["Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", "Angola", "Antarctica","Argentina","American Samoa","Austria","Australia","Aland Islands","Aruba","Azerbaidjan","Bosnia and Herzegovina", "Barbados", "Bangladesh", "Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Saint Barthelemey","Bermuda","Brunei Darussalam", "Bolivia", "Brazil", "Bahamas", "Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos Islands","Congo, Democratic Rebpulic of the","Central African Republic", "Congo","Switzerland","Ivory Coast","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba","Cape Verde","Christmas Island", "Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", "Algeria", "Ecuador", "Estonia", "Egypt", "Western Sahara", "Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands","Micronesia","Faroe Islands","France","Gabon","Great Britain", "Grenada", "Georgia", "French Guyana", "Guernsey", "Ghana", "Gibraltar", "Greenland"," Gambia", "Guinea", "Guadeloupe","Equatorial Guinea","Greece","S. Georgia & S. Sandwich Isls.", "Guatemala","Guam (USA)","Guinea Bissau","Guyana","Hong Kong","Heard and McDonald Islands", "Honduras", "Croatia", "Haiti", "Hungary", "Indonesia","Ireland","Israel","Isle of Man","India","British Indian Ocean Territory", "Iraq", "Iran", "Iceland", "Italy", "Jersey", "Jamaica", "Jordan","Japan","Kenya","Kyrgyz Republic (Kyrgyzstan)","Cambodia, Kingdom of","Kiribati","Comoros","Saint Kitts & Nevis Anguilla","North Korea", "South Korea","Kuwait","Cayman Islands","Kazakhstan","Laos","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg","Latvia","Libya","Morocco","Monaco","Moldavia","Montenegro","Saint Martin (French)","Madagascar", "Marshall Islands", "Macedonia", "Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique (French)","Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives", "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia (French)","Niger","Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","Polynesia (French)","Papua New Guinea", "Philippines", "Pakistan", "Poland","Saint Pierre and Miquelon","Pitcairn Island","Puerto Rico","Palestinian Territory (Occupied)","Portugal", "Palau", "Paraguay", "Qatar", "Reunion (French)","Romania","Serbia","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands", "Seychelles", "Sudan", "Sweden", "Singapore","Saint Helena","Slovenia","Svalbard Jan Mayen Islands","Slovak Republic","Sierra Leone","San Marino", "Senegal", "Somalia", "Suriname", "Saint Tome and Principe","El Salvador","Syria","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories", "Togo", "Thailand", "Tadjikistan", "Tokelau","Timor-Leste","Turkmenistan","Tunisia","Tonga","Turkey","Trinidad and Tobago", "Tuvalu", "Taiwan", "Tanzania", "Ukraine", "Uganda", "United Kingdom","USA Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent & Grenadines", "Venezuela","Virgin Islands (British)","Virgin Islands (USA)","Vietnam","Vanuatu","Wallis and Futuna Islands", "Samoa", "Yemen", "Mayotte","South Africa","Zambia","Zimbabwe"]
},
"06Email": {
"Default": "",
"Description": "UTM admin email",
"Type": "String"
},
"07Organization" : {
"Description" : "Name of your Organization",
"Type" : "String"
},
"08AdminPassword" : {
"Description" : "UTM admin password",
"Type" : "String",
"NoEcho" : "True"
},
"09KeyName" : {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access.",
"Type": "AWS::EC2::KeyPair::KeyName",
"Default" : ""
},
"10TrustedNetwork" : {
"Description" : "Trusted network. Only connections from this network are allowed to ports 22 and 8080. E.g 92.198.130.0/24",
"Type" : "String",
"Default" : "0.0.0.0/0"
},
"VPC" : {
"Description" : "VPC Id to deploy resources into",
"Type" : "String"
},
"Subnet1" : {
"Description" : "Public_Subnet_AZ1_Outside",
"Type" : "String"
},
"Subnet2" : {
"Description" : "Public_Subnet_AZ2_Outside",
"Type" : "String"
},
"ExistingS3Bucket" : {
"Description" : "Optional. The S3 Bucket to store and restore backups. If left empty a new bucket will be created automatically.",
"Type" : "String",
"Default": ""
},
"ExistingElasticIP" : {
"Description" : "Optional. The Elastic IP to assign in the UTM instance. If left empty a new Elastic IP will be allocated automatically.",
"Type" : "String",
"Default": ""
},
"LicensePool" : {
"Description" : "Optional. S3 bucket where the licenses are stored",
"Type" : "String",
"Default": ""
}
},
"Conditions" : {
"CreateS3Bucket" : { "Fn::Equals" : [{"Ref" : "ExistingS3Bucket"}, ""] },
"AllocateElasticIP" : { "Fn::Equals" : [{"Ref" : "ExistingElasticIP"}, ""] }
},
"Resources": {
"PublicNetworkAcl" : {
"Type" : "AWS::EC2::NetworkAcl",
"Properties" : {
"VpcId" : { "Ref" : "VPC" },
"Tags" : [
{"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
{"Key" : "Network", "Value" : "Public" }
]
}
},
"InboundHTTPPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "100",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "false",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "80", "To" : "80"}
}
},
"InboundDynamicPortsPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "200",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "false",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "1024", "To" : "65535"}
}
},
"InboundSSHPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "300",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "false",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "22", "To" : "22"}
}
},
"InboundRedUDPPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "400",
"Protocol" : "17",
"RuleAction" : "allow",
"Egress" : "false",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "3410", "To" : "3410"}
}
},
"OutboundSSHPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "105",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "22", "To" : "22"}
}
},
"OutboundHTTPPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "205",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "80", "To" : "80"}
}
},
"OutBoundDynamicPortPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "305",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "1024", "To" : "65535"}
}
},
"OutboundHTTPSPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "405",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "443", "To" : "443"}
}
},
"OutboundRedPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "406",
"Protocol" : "6",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "3000", "To" : "3000"}
}
},
"OutboundRedUDPPublicNetworkAclEntry" : {
"Type" : "AWS::EC2::NetworkAclEntry",
"Properties" : {
"NetworkAclId" : {"Ref" : "PublicNetworkAcl"},
"RuleNumber" : "407",
"Protocol" : "17",
"RuleAction" : "allow",
"Egress" : "true",
"CidrBlock" : "0.0.0.0/0",
"PortRange" : {"From" : "3410", "To" : "3410"}
}
},
"IPAddress" : {
"Type" : "AWS::EC2::EIP",
"Condition": "AllocateElasticIP",
"Properties" : {
"Domain": "vpc"
}
},
"S3Bucket" : {
"Type" : "AWS::S3::Bucket",
"Condition" : "CreateS3Bucket",
"DeletionPolicy" : "Retain",
"Properties" : {
"LifecycleConfiguration" : {
"Rules" : [
{
"Prefix" : "confd_backup",
"ExpirationInDays" : "3",
"Status" : "Enabled"
},
{
"Prefix" : "postgres_basebackup",
"ExpirationInDays" : "3",
"Status" : "Enabled"
},
{
"Prefix" : "postgres_wal",
"ExpirationInDays" : "3",
"Status" : "Enabled"
}
]
}
}
},
"UTMRole" : {
"Type" : "AWS::IAM::Role",
"Properties" : {
"AssumeRolePolicyDocument" : {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": [ "ec2.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
}]
},
"Path": "/",
"Policies": [{
"PolicyName" : "UTMPolicy",
"PolicyDocument" : {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*"
},
{
"Effect": "Allow",
"NotAction": "iam:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "cloudformation:UpdateStack",
"Resource": "*"
}]
}
}]
}
},
"UTMInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [{
"Ref": "UTMRole"
}]
}
},
"UTMSecurityGroup": {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Identifying security group",
"VpcId" : { "Ref" : "VPC" }
}
},
"TrustedNetworkGroup": {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable TCP access from trusted network",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "0",
"ToPort" : "65535",
"CidrIp" : { "Ref" : "10TrustedNetwork"}
} ],
"VpcId" : { "Ref" : "VPC" }
}
},
"UntrustedGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Untrusted network restricted from accessing port 22 and 8080.",
"SecurityGroupIngress" : [
{
"IpProtocol" : "tcp",
"FromPort" : "12",
"ToPort" : "21",
"CidrIp" : "0.0.0.0/0"
},
{
"IpProtocol" : "tcp",
"FromPort" : "23",
"ToPort" : "8079",
"CidrIp" : "0.0.0.0/0"
},
{
"IpProtocol" : "tcp",
"FromPort" : "8081",
"ToPort" : "65535",
"CidrIp" : "0.0.0.0/0"
},
{
"IpProtocol" : "udp",
"FromPort" : "3410",
"ToPort" : "3410",
"CidrIp" : "0.0.0.0/0"
}
],
"VpcId" : { "Ref" : "VPC" }
}
},
"UTMScalingGroup": {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"UpdatePolicy" : {
"AutoScalingRollingUpdate" : {
"MinInstancesInService" : "1",
"MaxBatchSize" : "1",
"PauseTime" : "PT5M"
}
},
"Properties" : {
"DesiredCapacity" : "1",
"MaxSize" : "2",
"MinSize" : "1",
"TerminationPolicies" : [
"NewestInstance"
],
"VPCZoneIdentifier" : [
{ "Ref" : "Subnet1" },
{ "Ref" : "Subnet2" }
],
"LaunchConfigurationName" : {"Ref": "UTMLaunchConfiguration"},
"NotificationConfiguration" : {
"TopicARN" : { "Ref" : "UnhealthyTopic" },
"NotificationTypes" : [
"autoscaling:EC2_INSTANCE_LAUNCH",
"autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
"autoscaling:EC2_INSTANCE_TERMINATE",
"autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
]
},
"Tags" : [{
"Key" : "Name",
"Value" : "HA-UTM",
"PropagateAtLaunch" : "true"
}]
}
},
"UTMLaunchConfiguration": {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Properties" : {
"AssociatePublicIpAddress" : true,
"IamInstanceProfile" : { "Ref" : "UTMInstanceProfile" },
"ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, {"Ref": "01LicenseType"} ]},
"InstanceType" : { "Ref" : "02InstanceType" },
"KeyName" : { "Ref" : "09KeyName" },
"BlockDeviceMappings" : [{
"DeviceName" : "/dev/sda",
"Ebs" : { "VolumeSize" : "100" }
}],
"SecurityGroups" : [
{ "Ref": "UTMSecurityGroup" },
{ "Ref": "TrustedNetworkGroup" },
{ "Ref": "UntrustedGroup" }
],
"UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [
"#!/bin/bash\n",
"echo AWS_REGION=",{ "Ref" : "AWS::Region" },">>/etc/environment\n",
"export AWS_REGION=",{ "Ref" : "AWS::Region" },"\n",
"echo EC2_URL=",{ "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "EC2API" ]},">>/etc/environment\n",
"export EC2_URL=",{ "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "EC2API" ]},"\n",
"date +'UserData start %c' > /tmp/user_data.log\n",
"echo 'version: \"0.1.0\"' >> /etc/cloud/user_data.yml\n",
"echo 'instance_role: \"ha_standalone\"' >> /etc/cloud/user_data.yml\n",
"echo 'deployment_type: \"ha_warm_standby\"' >> /etc/cloud/user_data.yml\n",
"echo 'license_pool: \"", { "Ref": "LicensePool" }, "\"' >> /etc/cloud/user_data.yml\n",
"cc=`/usr/local/bin/confd-client.plx country_name_to_code \"",{ "Ref": "05Country" },"\"`\n",
"echo \"{ hostname => '", { "Ref": "03Hostname" }, "', organization => '", { "Ref": "07Organization"}, "', city => '", { "Ref" : "04City" },"', country => '$cc', email => '", { "Ref": "06Email" }, "', password => '", { "Ref": "08AdminPassword" }, "' }\" > /var/confd/var/storage/setup.ph\n",
"/etc/init.d/confd restart\n",
"sleep 5\n",
"echo 'elastic_ip: \"",
{ "Fn::If" : [
"AllocateElasticIP",
{ "Ref" : "IPAddress" },
{ "Ref" : "ExistingElasticIP" }
]
},"\"' >> /etc/cloud/user_data.yml\n",
"echo 's3_bucket: \"",
{ "Fn::If" : [
"CreateS3Bucket",
{ "Ref" : "S3Bucket" },
{ "Ref" : "ExistingS3Bucket" }
]
},"\"' >> /etc/cloud/user_data.yml\n",
"echo 'stack_name: \"", { "Ref": "AWS::StackName"} ,"\"' >> /etc/cloud/user_data.yml\n",
"date +'UserData confd config start %c' >> /tmp/user_data.log\n",
"echo '{' > /tmp/user_data.config\n",
"echo '\"cloudwatch\" => { \"status\" => 1 },' >> /tmp/user_data.config\n",
"echo '\"confd\" => { \"backup\" => 1, \"backup_interval\" => 300, \"restore\" => 1, \"restore_done\" => 0 },' >> /tmp/user_data.config\n",
"echo '\"instance_role\" => \"ha_standalone\",' >> /tmp/user_data.config\n",
"echo '\"postgres\" => { \"archive_timeout\" => 300, \"backup\" => 1, \"base_backup_interval\" => 3600, \"restore\" => 1 },' >> /tmp/user_data.config\n",
"echo '\"syslog\" => { \"backup\" => 1, \"restore\" => 1, \"restore_period\" => 8 },' >> /tmp/user_data.config\n",
"echo '\"s3_bucket\" => \"",
{ "Fn::If" : [
"CreateS3Bucket",
{ "Ref" : "S3Bucket" },
{ "Ref" : "ExistingS3Bucket" }
]
},"\",' >> /tmp/user_data.config\n",
"echo '\"stack_name\" => \"", { "Ref": "AWS::StackName"},"\",' >> /tmp/user_data.config\n",
"echo '\"elastic_ip\" => \"",
{ "Fn::If" : [
"AllocateElasticIP",
{ "Ref" : "IPAddress" },
{ "Ref" : "ExistingElasticIP" }
]
}, "\",' >> /tmp/user_data.config\n",
"echo '\"trusted_network\" => \"", { "Ref": "10TrustedNetwork"},"\"' >> /tmp/user_data.config\n",
"echo '}' >> /tmp/user_data.config\n",
"/usr/local/bin/confd-client.plx -noquote -stdin set \\'ha\\' \\'aws\\' < /tmp/user_data.config >> /tmp/user_data.log\n",
"date +'UserData confd config end %c' >> /tmp/user_data.log\n",
"date +'UserData ha aws start %c' >> /tmp/user_data.log\n",
"/etc/init.d/ha_aws start\n",
"date +'UserData awslogs agent setup start %c' >> /tmp/user_data.log\n",
"/usr/local/bin/awslogs-agent-setup-v1.0.py -n -r ", { "Ref" : "AWS::Region" }, " -c /etc/cloud/awslogs.conf >> /tmp/user_data.log\n",
"date +'UserData awslogs agent setup end %c' >> /tmp/user_data.log\n",
"/usr/local/bin/confd-client.plx trigger ha_aws\n",
"exit 0\n"
] ] } }
}
},
"IngressTrafficMinimal": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmDescription": "Terminate if the incoming traffic is suspiciously low",
"MetricName": "NetworkIn",
"Namespace": "AWS/EC2",
"Statistic": "Sum",
"Period": "60",
"EvaluationPeriods": "2",
"Threshold": "100",
"AlarmActions": [ { "Ref": "UnhealthyTopic" } ],
"Dimensions": [{
"Name": "AutoScalingGroupName",
"Value": { "Ref": "UTMScalingGroup" }
}],
"ComparisonOperator": "LessThanThreshold"
}
},
"EgressTrafficMinimal": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmDescription": "Terminate if the outgoing traffic is suspiciously low",
"MetricName": "NetworkOut",
"Namespace": "AWS/EC2",
"Statistic": "Sum",
"Period": "60",
"EvaluationPeriods": "2",
"Threshold": "100",
"AlarmActions": [ { "Ref": "UnhealthyTopic" } ],
"Dimensions": [ {
"Name": "AutoScalingGroupName",
"Value": { "Ref": "UTMScalingGroup" }
}],
"ComparisonOperator": "LessThanThreshold"
}
},
"UnhealthyTopic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"Subscription": [{
"Endpoint": { "Ref": "06Email" },
"Protocol": "email"
}]
}
}
},
"Outputs" : {
"PublicIPAddress" : {
"Value" : { "Ref" : "IPAddress" },
"Description" : "Use this IP to connect and to forward traffic to the UTM."
},
"ScalingGroup" : {
"Value" : { "Ref": "UTMScalingGroup" },
"Description" : "The HA Scaling group."
},
"S3Bucket" : {
"Value" : { "Ref": "S3Bucket" },
"Description" : "The S3 Bucket."
}
}
}
https://www.sophos.com/en-us/support/knowledgebase/122202.aspx
from the looks of it confd-client.plx does most of the magic (via the user_data.config that is builds as you've guessed in the user data portion).
The UTMRole has the permissions to basically bind the elastic ip to the instance.
as a side note, you cannot (easily) associate elastic IPs to autoscalling groups (because by their nature they are ephemeral). The autoscalling is basically associated with the LaunchConfig in the template.
also as a side note this is highly specific to the Sophos stuff and it's not how one would typically go about using Cloudformation on AWS.