CloudFormation WaitCondition requires public access - amazon-web-services

I have a CloudFormation script which deploys a single instance in the VPC.
The script only works when the instance has a public IP. Without a public IP address, the template deployment fails at WaitCondition stage. I guess thats because the WaitCondition requires instance has access to the Internet?
Instead of assigning a public IP, will it work if I set the proxy address on the server? So the instance access the Internet via web proxy. If that's the case, what's the web URLs need to be whitelisted on the proxy? and can I add the add proxy address command in the userdata?
Below is my template:
"Mappings": {
"AWSRegion2AMI": {
"ap-southeast-2": {
"Windows2008r2": "ami-27b39a44",
"Windows2012r2": "ami-83b198e0"
}
}
},
"Resources": {
"DomainController": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"config": {
"files": {
"c:\\cfn\\cfn-hup.conf": {
"content": {
"Fn::Join": ["", [
"[main]\n",
"stack=", {
"Ref": "AWS::StackId"
}, "\n",
"region=", {
"Ref": "AWS::Region"
}, "\n"
]]
}
},
"c:\\cfn\\hooks.d\\cfn-auto-reloader.conf": {
"content": {
"Fn::Join": ["", [
"[cfn-auto-reloader-hook]\n",
"triggers=post.update\n",
"path=Resources.DomainController.Metadata.AWS::CloudFormation::Init\n",
"action=cfn-init.exe -v -s ", {
"Ref": "AWS::StackId"
},
" -r DomainController",
" --region ", {
"Ref": "AWS::Region"
}, "\n"
]]
}
},
"C:\\cfn\\RunCommand.bat": {
"content": "%~1\nIF %ERRORLEVEL% GTR 10 ( exit /b 1 ) else ( exit /b 0 )"
}
},
"commands": {
"1-run-dcpromo": {
"command": {
"Fn::Join": ["", [
"C:\\cfn\\RunCommand.bat \"dcpromo /unattend /ReplicaOrNewDomain:Domain /NewDomain:Forest /NewDomainDNSName:", {
"Ref": "DomainDNSName"
},
" /ForestLevel:4 /DomainNetbiosName:", {
"Ref": "DomainNetBIOSName"
},
" /DomainLevel:4 /InstallDNS:Yes /ConfirmGc:Yes /CreateDNSDelegation:No /DatabasePath:\"C:\\Windows\\NTDS\" /LogPath:\"C:\\Windows\\NTDS\" /SYSVOLPath:\"C:\\Windows\\SYSVOL\" /SafeModeAdminPassword=", {
"Ref": "RestoreModePassword"
},
" /RebootOnCompletion:Yes\""
]]
},
"waitAfterCompletion": "forever"
},
"2-signal-success": {
"command": {
"Fn::Join": ["", [
"cfn-signal.exe -e 0 \"", {
"Fn::Base64": {
"Ref": "DomainControllerWaitHandle"
}
}, "\""
]]
}
}
},
"services": {
"windows": {
"cfn-hup": {
"enabled": "true",
"ensureRunning": "true",
"files": ["c:\\cfn\\cfn-hup.conf", "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf"]
}
}
}
}
}
},
"Properties": {
"ImageId": {
"Fn::FindInMap": ["AWSRegion2AMI", {
"Ref": "AWS::Region"
}, "Windows2008r2"]
},
"InstanceType": {
"Ref": "InstanceType"
},
"NetworkInterfaces": [{
"AssociatePublicIpAddress": "false",
"DeviceIndex": "0",
"SubnetId": {
"Ref": "SubnetId"
}
}],
"KeyName": {
"Ref": "KeyName"
},
"UserData": {
"Fn::Base64": {
"Fn::Join": ["", [
"<script>\n",
"cfn-init.exe -v -s ", {
"Ref": "AWS::StackId"
},
" -r DomainController ",
" --region ", {
"Ref": "AWS::Region"
}, "\n",
"</script>"
]]
}
}
}
},
"DomainControllerWaitCondition": {
"Type": "AWS::CloudFormation::WaitCondition",
"DependsOn": "DomainController",
"Properties": {
"Handle": {
"Ref": "DomainControllerWaitHandle"
},
"Timeout": "1500"
}
},
"DomainControllerWaitHandle": {
"Type": "AWS::CloudFormation::WaitConditionHandle"
}

Pass in the proxy by adding on of those parameters to cfn-signal.exe:
--http-proxy
An HTTP proxy (non-SSL). Use the following format: http://user:password#host:port
--https-proxy
An HTTPS proxy. Use the following format: https://user:password#host:port
The signal is sent to an S3 Bucket. You can whitelist something like:
https://cloudformation-waitcondition-*.s3.amazonaws.com
Note: You can also use the S3 VPC endpoint feature to allow resources inside private subnets to access S3. http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-endpoints.html

Related

Configsets config commands are not executing sequencially cloudformation

This is metadata for cloudformationinit
{
"Metadata": {
"AWS::CloudFormation::Init": {
"configSets": {
"setup": [
"setup_docker",
"setup_redis"
]
},
"setup_docker": {
"commands": {
"install_docker": {
"command": {
"Fn::Join": ["", ["amazon-linux-extras install docker"]]
}
},
"start_docker": {
"command": { "Fn::Join": ["", ["service docker start"]] }
},
"start_on_restart": {
"command": { "Fn::Join": ["", ["systemctl enable docker"]] }
}
}
},
"setup_redis": {
"commands": {
"install_redis": {
"command": {
"Fn::Join": ["", ["amazon-linux-extras install redis6"]]
}
},
"change_redis_6379_protected_mode": {
"command": {
"Fn::Join": [
"",
[
"sed -i 's/protected-mode yes/protected-mode no/' /etc/redis/redis.conf"
]
]
}
},
"change_redis_6379_daemonize": {
"command": {
"Fn::Join": [
"",
["sed -i 's/daemonize no/daemonize yes/' /etc/redis/redis.conf"]
]
}
},
"start_redis": {
"command": { "Fn::Join": ["", ["systemctl start redis"]] }
}
}
}
}
}
}
When I execute this template, setup_docker commands got executed in order, first it will execute install_docker, then start_docker, then start_on_restart command, but setup_redis commands are not executing, first two commands got skipped, third command got executed and it throws an error /etc/redis/redis.conf file not found
Why this is happening? commands are wrapped inside setup_redis object only and what is the solution for this?
Thanks in advance
Update
If I separate out the installation part of redis into separate config, it is working properly, does this mean command will not execute sequentially? Why commands are not executing sequentially?
{
"Metadata": {
"AWS::CloudFormation::Init": {
"configSets": {
"setup": ["setup_redis", "modify_files"]
},
"setup_redis": {
"commands": {
"install_redis": {
"command": {
"Fn::Join": ["", ["amazon-linux-extras install redis6"]]
}
}
}
},
"modify_files": {
"commands": {
"change_redis_6379_protected_mode": {
"command": {
"Fn::Join": [
"",
[
"sed -i 's/protected-mode yes/protected-mode no/' /etc/redis/redis.conf"
]
]
}
},
"change_redis_6379_daemonize": {
"command": {
"Fn::Join": [
"",
["sed -i 's/daemonize no/daemonize yes/' /etc/redis/redis.conf"]
]
}
},
"start_redis": {
"command": { "Fn::Join": ["", ["systemctl start redis"]] }
}
}
}
}
},
"Properties": {
"ImageId": {
"Ref": "AMI"
},
"KeyName": {
"Ref": "Key"
},
"InstanceType": {
"Ref": "InstanceTypeParameter"
},
"SubnetId": {
"Ref": "SubnetIdParameter"
},
"SecurityGroupIds": {
"Ref": "SecurityGroupIdsParameter"
},
"PrivateIpAddress": "10.0.0.57",
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe",
"\n",
"yum update -y",
"\n",
"/opt/aws/bin/cfn-init -v",
" --stack ",
{ "Ref": "AWS::StackName" },
" --resource DevInstance",
" --configsets setup",
" --region ",
{ "Ref": "AWS::Region" },
"\n",
"/opt/aws/bin/cfn-signal -e $?",
" --stack ",
{ "Ref": "AWS::StackName" },
" --resource DevInstance",
" --region ",
{ "Ref": "AWS::Region" },
"\n"
]
]
}
}
}
}
Commands are processed in alphabetical order by name. So in your original configuration, they were going to be executed in this order:
change_redis_6379_daemonize
change_redis_6379_protected_mode
install_redis
start_redis
Separating out the installation part to make it execute first is the correct solution.
Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html#aws-resource-init-commands

Why am I getting an error that there are unresolved VPC dependencies?

I'm trying to implement configuration management that meets the following criteria for an assighment:
https://s3.amazonaws.com/seis615/AnsiblePress.json
Take a quick look at the template in a text editor. Notice how the UserData property for the mgmt1 instance is configured. When CloudFormation launches this stack, it will automatically install and configure Ansible software on the management server. It’s very common to use a small amount of scripting code to bootstrap configuration management software onto a new system. Once Ansible is installed it can be used to install and configure other servers in the environment.
The CloudFormation template is missing a couple resources that you will need to add:
An application load balancer with a logical name of webserverlb which distributes HTTP (port 80) requests to the web1 and web2 instances. The health check endpoint for the load balancer should be the root (/) directory.
A db.t2.micro RDS database instance (not a cluster) running a MariaDB 10.2.21 database called wordpress located in a private VPC subnet. Use the logical name wordpressdb for the CloudFormation RDS resource. RDS and EC2 instances actually pre-date the arrival of VPCs in AWS so confusingly there are two different ways to configure these resources. You need to make sure this database instance is designed to run inside a VPC with the proper database subnet group and security group resources defined.
A security group called WebserverLbSecurityGroup which allows incoming http access from the Internet.
A security group called WordpressDbSecurityGroup which allows incoming access on the standard MySQL port from the WebServerSecurityGroup
An input parameter called DBName which will define the database name to create (default to wordpress)
An input parameter called DBUser which will be used for the database server username.
An input parameter called DBPassword which will be used for the database server password.
A stack output called wordpressDbEndpoint which shows the MariaDB instance endpoint address.
A stack output called wordpressLbEndpoint which shows the application load balancer URL.
The JSON I've configured (below) gives me the following template format error and I don't know why:
Template format error: Unresolved resource dependencies [wordpressVPC] in the Resources block of the template
{"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"SSMAccessRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Principal": {
"Service": [ "ec2.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
} ]
},
"Path": "/"
}
},
"SSMRolePolicies": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "ssmProperties",
"PolicyDocument": {
"Version" : "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:DescribeParameters",
"ssm:PutParameter",
"ssm:GetParameters",
"ssm:DeleteParameter"
],
"Resource": {
"Fn::Join" : [
"",
[
"arn:aws:ssm:",
{ "Ref" : "AWS::Region" },
":",
{ "Ref" : "AWS::AccountId"},
{
"Fn::Join" : [
"",
[ ":parameter/", { "Ref": "AWS::StackName" }, ".*" ]
]
}
]
]
}
}
]
},
"Roles": [ { "Ref": "SSMAccessRole" } ]
}
},
"SSMInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [ { "Ref": "SSMAccessRole" } ]
}
},
"web1pem" : {
"Type" : "AWS::SSM::Parameter",
"Properties" : {
"Name" : {
"Fn::Join" : [
"",
[ { "Ref": "AWS::StackName" }, ".web1pem" ]
]
},
"Type" : "String",
"Value" : "0",
"Description": "web1 instance private key."
}
},
"web2pem" : {
"Type" : "AWS::SSM::Parameter",
"Properties" : {
"Name" : {
"Fn::Join" : [
"",
[ { "Ref": "AWS::StackName" }, ".web2pem" ]
]
},
"Type" : "String",
"Value" : "0",
"Description": "web2 instance private key."
}
},
"wordpressVpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"EnableDnsSupport": "true",
"EnableDnsHostnames": "true",
"CidrBlock": "10.0.0.0/16",
"Tags": [
{
"Key": "Environment",
"Value": "Test"
}
]
}
},
"publicSubnet1": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
},
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone" : {
"Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]
}
}
},
"publicSubnet2": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
},
"CidrBlock": "10.0.1.0/24",
"AvailabilityZone" : {
"Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]
}
}
},
"privateSubnet1": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
},
"CidrBlock": "10.0.2.0/24",
"AvailabilityZone" : {
"Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]
}
}
},
"privateSubnet2": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
},
"CidrBlock": "10.0.3.0/24",
"AvailabilityZone" : {
"Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]
}
}
},
"web1": {
"Type": "AWS::EC2::Instance",
"DependsOn": [
"web1pem"
],
"Properties": {
"InstanceType": "t2.micro",
"ImageId": {"Ref": "AMI"},
"IamInstanceProfile": {
"Ref": "SSMInstanceProfile"
},
"KeyName": {
"Ref": "KeyName"
},
"NetworkInterfaces": [
{
"GroupSet": [
{
"Ref": "WebServerSecurityGroup"
}
],
"AssociatePublicIpAddress": "true",
"DeviceIndex": "0",
"DeleteOnTermination": "true",
"SubnetId": {
"Ref": "publicSubnet1"
}
}
],
"Tags": [
{
"Key": "Name",
"Value": "web1"
}
],
"UserData" : {
"Fn::Base64" : {
"Fn::Join" : [
"", [
"#!/bin/bash -xe\n",
"ssh-keygen -f /home/ec2-user/.ssh/web1-key.pem -q -N \"\"\n",
"chown ec2-user:ec2-user /home/ec2-user/.ssh/web1-key.pem\n",
"chown ec2-user:ec2-user /home/ec2-user/.ssh/web1-key.pem.pub\n",
"PEMFILE=`cat /home/ec2-user/.ssh/web1-key.pem`\n",
"aws ssm put-parameter --name ", { "Ref" : "web1pem" }, " --type String --value \"${PEMFILE}\" --overwrite --region ", { "Ref" : "AWS::Region" },"\n",
"cat /home/ec2-user/.ssh/web1-key.pem.pub >> /home/ec2-user/.ssh/authorized_keys\n",
"# Signal the status from cfn-init\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ",
{
"Ref": "AWS::StackName"
},
" --resource web1 ",
" --region ",
{
"Ref": "AWS::Region"
},
"\n"
]
]
}
}
},
"CreationPolicy": {
"ResourceSignal": {
"Timeout": "PT5M"
}
}
},
"web2": {
"Type": "AWS::EC2::Instance",
"DependsOn": [
"web1pem"
],
"Properties": {
"InstanceType": "t2.micro",
"ImageId": {"Ref": "AMI"},
"IamInstanceProfile": {
"Ref": "SSMInstanceProfile"
},
"KeyName": {
"Ref": "KeyName"
},
"NetworkInterfaces": [
{
"GroupSet": [
{
"Ref": "WebServerSecurityGroup"
}
],
"AssociatePublicIpAddress": "true",
"DeviceIndex": "0",
"DeleteOnTermination": "true",
"SubnetId": {
"Ref": "publicSubnet2"
}
}
],
"Tags": [
{
"Key": "Name",
"Value": "web2"
}
],
"UserData" : {
"Fn::Base64" : {
"Fn::Join" : [
"", [
"#!/bin/bash -xe\n",
"ssh-keygen -f /home/ec2-user/.ssh/web2-key.pem -q -N \"\"\n",
"chown ec2-user:ec2-user /home/ec2-user/.ssh/web2-key.pem\n",
"chown ec2-user:ec2-user /home/ec2-user/.ssh/web2-key.pem.pub\n",
"PEMFILE=`cat /home/ec2-user/.ssh/web2-key.pem`\n",
"aws ssm put-parameter --name ", { "Ref" : "web2pem" }, " --type String --value \"${PEMFILE}\" --overwrite --region ", { "Ref" : "AWS::Region" },"\n",
"cat /home/ec2-user/.ssh/web2-key.pem.pub >> /home/ec2-user/.ssh/authorized_keys\n",
"# Signal the status from cfn-init\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ",
{
"Ref": "AWS::StackName"
},
" --resource web2 ",
" --region ",
{
"Ref": "AWS::Region"
},
"\n"
]
]
}
}
},
"CreationPolicy": {
"ResourceSignal": {
"Timeout": "PT5M"
}
}
},
"WebServerSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
},
"GroupDescription": "Allow access from HTTP and SSH traffic",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": {"Ref": "YourIp"}
}
]
}
},
"WebServerSGIngressTCP22": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Metadata": {
"Comment": "SSH ingress security rule"
},
"Properties" : {
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"SourceSecurityGroupId": { "Ref": "WebServerSecurityGroup" },
"GroupId": { "Fn::GetAtt": ["WebServerSecurityGroup", "GroupId"]}
}
},
"InternetGateway": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {}
},
"AttachGateway": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"InternetGatewayId": {
"Ref": "InternetGateway"
},
"VpcId": {
"Ref": "wordpressVpc"
}
}
},
"PublicRouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "wordpressVpc"
}
}
},
"PublicRoute": {
"Type": "AWS::EC2::Route",
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": {
"Ref": "PublicRouteTable"
},
"GatewayId": {
"Ref": "InternetGateway"
}
},
"DependsOn": [
"InternetGateway", "AttachGateway"
]
},
"Public1RouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": {
"Ref": "PublicRouteTable"
},
"SubnetId": {
"Ref": "publicSubnet1"
}
}
},
"Public2RouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": {
"Ref": "PublicRouteTable"
},
"SubnetId": {
"Ref": "publicSubnet2"
}
}
},
"webserverlb": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"IpAddressType": "ipv4",
"SecurityGroups": [
{
"Ref": "webserverlbSecurityGroup"
}
],
"Subnets": [
{
"Ref": "publicSubnet1"
},
{
"Ref": "publicSubnet2"
}
],
"Tags": [
{
"Key": "Name",
"Value": "webserverlb"
}
]
},
"DependsOn": [
"webserversSecurityGroup"
]
},
"webserverlbSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Ref": "wordpressVPC"
},
"GroupDescription": "Allows incoming requests from port 80 via HTTP.",
"SecurityGroupIngress": [
{
"IpProtocol": "TCP",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "0.0.0.0/0",
"Description": "Allows 80 from Internet"
}
]
}
},
"wordpressdb": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"VpcId": {
"Ref": "wordpressVPC"
},
"AvailabilityZone": "us-east-1a",
"DBInstanceClass": "db.t2.micro",
"DBName": "wordpress",
"Engine": "mariadb",
"EngineVersion": "10.2.21",
"MultiAZ": 1,
"Tags": [
{
"Key": "Name",
"Value": "wordpressdb"
}
]
},
"DependsOn": [
"wordpressdbSecurityGroup"
]
},
"wordpressdbSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"VpcId": {
"Ref": "wordpressVPC"
},
"GroupDescription": "Enable access to the db via port 3306.",
"Tags": [
{
"Key": "Name",
"Value": "wordpressdbSecurityGroup"
}
],
"SecurityGroupIngress": [
{
"IpProtocol": "TCP",
"FromPort": "3306",
"ToPort": "3306",
"Description": "Enable HTTP access."
}
]
}
}
},
"Parameters": {
"KeyName": {
"Description": "Name of your EC2 KeyPair to enable SSH access to the instances.",
"Type": "AWS::EC2::KeyPair::KeyName",
"ConstraintDescription": "must be the name of an existing EC2 KeyPair."
},
"YourIp": {
"Description": "The current CIDR IP address of your workstation (x.x.x.x/32). http://checkip.amazonaws.com/",
"Type": "String",
"AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-9]|3[0-2]))$",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x."
},
"AMI": {
"Description": "The EC2 instance AMI",
"Type": "String",
"Default": "ami-00dc79254d0461090"
},
"DBName": {
"Description": "Name of the database",
"Type" : "String",
"Default": "wordpress"
},
"DBUser": {
"Default": "admin",
"NoEcho": "false",
"Description" : "The WordPress database admin account user name",
"Type": "String",
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*"
},
"DBPassword": {
"NoEcho": "true",
"Description" : "The password of the database.",
"Type": "String",
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*"
}
},
"Outputs": {
"web1PublicIp": {
"Value": {"Fn::GetAtt": ["web1","PublicIp"]},
"Description": "web1 public IP"
},
"we2PublicIp": {
"Value": {"Fn::GetAtt": ["web2","PublicIp"]},
"Description": "web2 public IP"
},
"mgmt1PublicIp": {
"Value": {"Fn::GetAtt": ["mgmt1","PublicIp"]},
"Description": "mgmt1 public IP"
}
}
}
Because CloudFormation is case sensitive. Your vpc resource is called wordpressVpc, but in some places you are using wordpressVPC.
Recommend trying the CloudFormation Linter in VSCode to see some of these errors inline while authoring templates along with autocompletion and documentation links:
E3005 DependsOn should reference other resources at Resources/webserverlb/DependsOn/0
E1012 Ref wordpressVPC not found as a resource or parameter
E1012 Ref wordpressVPC not found as a resource or parameter
E3002 Invalid Property Resources/wordpressdb/Properties/VpcId
E3003 Property DBSecurityGroupIngress missing at Resources/wordpressdbSecurityGroup/Properties
E1012 Ref wordpressVPC not found as a resource or parameter
E3002 Invalid Property Resources/wordpressdbSecurityGroup/Properties/VpcId
E3002 Invalid Property Resources/wordpressdbSecurityGroup/Properties/SecurityGroupIngress
E1010 Invalid GetAtt mgmt1.PublicIp for resource mgmt1PublicIp

Cloudformation - Using parameters in EC2 server

Below is my cloudformation template. Basically I want to use a parameter called "User" in a command in EC2 ubuntu server.
I have tried many solutions, but the mkdir command is not fails, rest of the template is working.
In the below example I have tried to set the "User" value as a tag as well as a property. Both failed. Suggest if you have seen any such examples.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"UserName": {
"Type": "String",
"Description": "Enter the username",
"AllowedPattern": "[a-zA-Z0-9_\\-]+",
"ConstraintDescription": "It should be a combination of characters from [A-Z],[a-z],[0-9],- and _."
},
"KeyName": {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
"Type": "AWS::EC2::KeyPair::KeyName",
"ConstraintDescription" : "must be the name of an existing EC2 KeyPair."
}
},
"Resources": {
"EC2Ansiblemaster2743": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"User" : { "Ref": "UserName" },
"AWS::CloudFormation::Init" : {
"configSets" : {
"CreateFile" : [
"create_file"
]
},
"create_file" : {
"files" : {
"/home/ubuntu/healthy.txt": {
"content": "Hello"
}
},
"commands" : {
"mkdir" : {
"command" : "sudo mkdir /home/$User"
}
}
}}
},
"Properties": {
"AvailabilityZone": "us-east-2a",
"ImageId": "ami-05c1fa8df71875112",
"InstanceType": "t2.micro",
"SourceDestCheck": false,
"KeyName" : { "Ref" : "KeyName" },
"Tags" : [
{
"Key" : "User",
"Value" : { "Ref": "UserName" }
}],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"sudo apt-get update\n",
"sudo apt-get -y install python-pip\n",
"sudo pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
"# Install the files and packages from the metadata\n",
"sudo /usr/local/bin/cfn-init ",
" --stack ",
{
"Ref": "AWS::StackName"
},
" --resource EC2Ansiblemaster2743 ",
" --configsets CreateFile ",
" --region ",
{
"Ref": "AWS::Region"
},
"\n",
"# Signal the status from cfn-init\n",
"sudo /usr/local/bin/cfn-signal -e $? ",
" --stack ",
{
"Ref": "AWS::StackName"
},
" --resource EC2Ansiblemaster2743 ",
" --region ",
{
"Ref": "AWS::Region"
},
"\n"
]
]
}
},
"SecurityGroupIds": [
{
"Ref": "EC2SG2AnsM0012743"
}
]
}
},
"EC2SG2AnsM0012743": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupName": "SGAnsibleMas2743",
"GroupDescription": "First",
"SecurityGroupIngress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "0",
"ToPort": "65535",
"IpProtocol": "tcp",
"Description": "From Anywhere"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "22",
"ToPort": "22",
"IpProtocol": "tcp",
"Description": "SSH from anywhere , from and to port will be same - 22"
}
]
},
}
}
}
I used the Fn:Sub to get the parameter to Userdata.
{
"Fn::Sub":
[
"UserName=${variable}\n",
{
"variable": {
"Ref": "UserName"
}
}]
Later I curl 169.254.169.254/latest/user-data to extract the Username string.
Please let me know if any easier solution is there for this issue.
Use the join intrinsic function to properly form the command string by referencing the username in the template.
"command" : "Fn::Join": [ "", "mkdir /home/", { "Ref": "UserName" }]
The issue is that your mkdir command does not resolve as you have it. There is no linkage between the username parameter and the command in the init section. By using the Join function I am forming the command with the correct parameter value.

AWS cloud formation cannot valid instance id

I am creating a cloudformation template in AWS. I am still getting an error:
WaitCondition received failed message: 'ValidationError:' for uniqueId: i-04885f92b0b4a99ab
I have tried change instance id. and many other way. none of these works.
The error occurred on AWS::CloudFormation::WaitCondition
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"KeyName": {
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances",
"Type": "AWS::EC2::KeyPair::KeyName"
},
"InstanceType": {
"Description": "FormEngine EC2 instance type",
"Type": "String",
"Default": "t2.micro"
}
},
"Mappings": {
"AWSInstanceType2Arch": {
"t2.micro": {
"Arch": "64"
}
},
"AWSRegionArch2AMI": {
"us-west-2": {
"64": "ami-f2d3638a"
}
}
},
"Resources": {
"WebServerGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Enable SSH and HTTP access",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "0.0.0.0/0"
}
]
}
},
"CfnUser": {
"Type": "AWS::IAM::User",
"Properties": {
"Path": "/",
"Policies": [
{
"PolicyName": "Admin",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
}
]
}
},
"HostKeys": {
"Type": "AWS::IAM::AccessKey",
"Properties": {
"UserName": {
"Ref": "CfnUser"
}
}
},
"WebServer": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"config": {
"packages": {
"yum": {
"java-1.6.0-openjdk": [],
"tomcat6": [],
"httpd": []
}
},
"files": {
"/var/www/html/index.html": {
"source": "http://a00807428-lab02/index.html",
"mode": "000600",
"owner": "apache",
"group": "apache"
},
"/var/www/html/index.html": {
"source": "http://a00807428-lab02/index.html",
"mode": "000600",
"owner": "apache",
"group": "apache"
}
}
}
}
},
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
{
"Fn::FindInMap": [
"AWSInstanceType2Arch",
{
"Ref": "InstanceType"
},
"Arch"
]
}
]
},
"InstanceType": {
"Ref": "InstanceType"
},
"SecurityGroups": [
{
"Ref": "WebServerGroup"
}
],
"KeyName": {
"Ref": "KeyName"
},
"Tags": [
{
"Key": "Name",
"Value": "WebServer"
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -v\n",
"date > /home/ec2-user/starttime\n",
"yum update -y aws-cfn-bootstrap\n",
"## Error reporting helper function\n",
"function error_exit\n",
"{\n",
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
{
"Ref": "WaitHandle"
},
"'\n",
" exit 1\n",
"}\n",
"## Initialize CloudFormation bits\n",
"/opt/aws/bin/cfn-init -v -s ",
{
"Ref": "AWS::StackId"
},
" -r FormEngine",
" --access-key ",
{
"Ref": "HostKeys"
},
" --secret-key ",
{
"Fn::GetAtt": [
"HostKeys",
"SecretAccessKey"
]
},
" --region ",
{
"Ref": "AWS::Region"
},
" > /tmp/cfn-init.log 2>&1 || error_exit $(</tmp/cfn-init.log)\n",
"# Configure Apache HTTPD\n",
"chkconfig httpd on\n",
"chkconfig --level 345 httpd on\n",
"# Start servers\n",
"/etc/init.d/httpd start\n",
"# Send signal to WaitHandle that the setup is completed\n",
"/opt/aws/bin/cfn-signal",
" -e 0",
" '",
{
"Ref": "WaitHandle"
},
"'",
"\n",
"date > /home/ec2-user/stoptime"
]
]
}
}
}
},
"WaitHandle": {
"Type": "AWS::CloudFormation::WaitConditionHandle"
},
"WaitCondition": {
"Type": "AWS::CloudFormation::WaitCondition",
"DependsOn": "WebServer",
"Properties": {
"Handle": {
"Ref": "WaitHandle"
},
"Timeout": "1200"
}
},
"IPAddress": {
"Type": "AWS::EC2::EIP"
},
"IPAssoc": {
"Type": "AWS::EC2::EIPAssociation",
"Properties": {
"InstanceId": {
"Ref": "WebServer"
},
"EIP": {
"Ref": "IPAddress"
}
}
}
},
"Outputs": {
"InstanceIPAddress": {
"Value": {
"Ref": "IPAddress"
},
"Description": "public IP address of the new WebServer"
},
"InstanceName": {
"Value": {
"Fn::GetAtt": [
"WebServer",
"PublicDnsName"
]
},
"Description": "public DNS name of the new WebServer"
}
}
}
succeed to created cloudformation.

CloudFormation template failing to create "files" under MetaData Section

Could somebody help me point what wrong am I doing here. I am failing to understand how the meta data part uses the authentication resource, In the AWS::CloudFormation::Authentication part I've mentioned the role same as the one attached to the instance, Yet I'm unable to create the file "some.txt"
{
"Parameters": {
"SecurityGroupId": {
"Description": "Security group for instance",
"Type": "AWS::EC2::SecurityGroup::Id"
}
},
"Resources": {
"MyInstance": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"configsets": {
"InstallIt": ["config1"]
},
"config1": {
"files": {
"/home/ec2-user/some.txt": {
"content": "This is my name ",
"encoding": "base64",
"mode": "000644",
"owner": "root",
"group": "root"
}
}
}
},
"AWS::CloudFormation::Authentication": {
"HelpMe": {
"type": "S3",
"buckets": "poc-bucket",
"roleName": "EC2andS3"
}
}
},
"Properties": {
"KeyName": "GoldenImage-NV-Anant",
"DisableApiTermination": "false",
"ImageId": "ami-0b33d91d",
"InstanceType": "t2.micro",
"Monitoring": "false",
"SubnetId": "subnet-73487a59",
"SecurityGroupIds": [{
"Ref": "SecurityGroupId"
}],
"IamInstanceProfile": {
"Ref": "MyInstanceProfile"
},
"Tags": [{
"Key": "Name",
"Value": "GeicoUserDataPocInstance"
}],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"", [
"#!/bin/bash -ex \n",
"echo \"hello dudes\" > /home/ec2-user/hello.txt \n",
"yum update -y aws-cfn-bootstrap\n",
"/opt/aws/bin/cfn-init -v",
" --stack ", {
"Ref": "AWS::StackId"
},
" --resource MyInstance ",
" --configsets InstallIt ",
" --region ", {
"Ref": "AWS::Region"
}, "\n",
"echo \"bye dudes\" > /home/ec2-user/bye.txt", "\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ", {
"Ref": "AWS::StackId"
},
" --resource MyInstance ",
" --region ", {
"Ref": "AWS::Region"
}, "\n"
]
]
}
}
},
"CreationPolicy": {
"ResourceSignal": {
"Timeout": "PT90M",
"Count": "1"
}
}
},
"MyInstanceProfile": {
"Description": "Instance profile for the instance",
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": ["EC2andS3"]
}
}
}
}
configsets should be configSets with capital S:
"configSets": {
"InstallIt": ["config1"]
},
buckets property needs to be a list of strings (this might not be necessary, the documentation is a bit unclear):
"buckets": ["poc-bucket"]
AWS::CloudFormation::Authentication resource shouldn't be necessary unless the source of your file is an S3 bucket. Even then, it still shouldn't be necessary when using an attached instance profile, since it will use the instance profile for authentication by default.