I want my CloudFormation template to use existing subnets and VPCs. I don't want to create new ones.
How do I parameterize these?
When I look at the docs for AWS::EC2::VPC and AWS::EC2::Subnet, it seems these resources are only for creating new VPCs and subnets. Is that correct?
Should I just point the instance resource directly to the existing VPC and subnets I want it to use?
For example - if I have an instance resource in my template and I point it directly to an existing subnet, like this:
{
"Resources": {
"MyServer": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": {
"Ref": "InstanceType"
},
"SubnetId": {
"Ref": "subnet-abc123"
},
...
I get this error when validating the template:
Template contains errors.: Template format error: Unresolved resource dependencies [subnet-abc123] in the Resources block of the template
I tried to do this with mappings but still getting an error:
"Mappings": {
"SubnetID": {
"TopKey": {
"Default": "subnet-abc123"
}
}
And with this in the instance resource:
"SubnetId": {
"Fn::FindInMap": [
"SubnetID",
{
"Ref": "TopKey"
},
"Default"
]
}
I get this error when trying to validate:
Template contains errors.: Template format error: Unresolved resource dependencies [TopKey] in the Resources block of the template
If you wish to use a specific VPC and subnet, just insert their values:
{
"Resources": {
"MyServer": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"SubnetId": "subnet-abc123",
"ImageId": "ami-abcd1234"
}
}
}
A subnet always belongs to a VPC, so specifying the subnet will automatically select the matching VPC.
Specify them in the Parameters section, and refer them in Resources section. CF will let you select the VPC first and then the Subnet.
"Parameters" : {
"VpcId" : {
"Type" : "AWS::EC2::VPC::Id",
"Description" : "VPCId of Virtual Private Cloud (VPC).",
"Default" : ""
},
"VpcSubnet": {
"Description" : "SubnetId in VPC",
"Type" : "AWS::EC2::Subnet::Id",
"Default" : ""
},
"Resources" : {
...
"Ec2Instance" : {
"Properties" : {
"SubnetId" : { "Ref" : "VpcSubnet" },
Related
I'm using this cloudformation template to create capacity providers for ECS cluster with the autoscaling group specified in the ecs capacity provider:
"ECSCapacityProvider": {
"Type": "AWS::ECS::CapacityProvider",
"Properties": {
"AutoScalingGroupProvider": {
"AutoScalingGroupArn": { "Ref" : "AutoScalingGroup" }
}
},
"DependsOn" : "AutoScalingGroup"
},
"DRCluster": {
"Type": "AWS::ECS::Cluster",
"Properties": {
"ClusterName": { "Ref" : "WindowsECSCluster" },
"CapacityProviders" : "ECSCapacityProvider",
"Tags": [
{
"Key": "environment",
"Value": "dr"
}
]
},
"DependsOn" : "ECSCapacityProvider"
}
But while creating the stack it resulted in the following error:
Model validation failed (#/CapacityProviders: expected type: JSONArray, found: String)
I could not find proper documentation for the capacity providers. I'm using it to attach the Auto Scaling group to the cluster, which i hope is the correct way to do so. I'm new to cloudformation, any help is much appreciated.
CapacityProviders is a List of String, not a String like you have now:
"CapacityProviders" : "ECSCapacityProvider",
Therefore, in you DRCluster you can use the following instead:
"CapacityProviders" : [ {"Ref": "ECSCapacityProvider"} ],
I am trying to create VPN gateway and route table entry which need VPN gateway as dependency. Due to race condition, route table entry (i.e. RouteA), is unable to find the VPN gateway by the time of its creation. I tried adding WaitCondition to solve this issue, but it needs a success condition. Looking at AWS documentation, I couldn't find any way to send success signal after creation of VPN gateway. Here is the code:
"VPNGateway" : {
"Type" : "AWS::EC2::VPNGateway",
"Properties" : {
"Type" : "ipsec.1"
}
},
"WaitHandle" : {
"Type" : "AWS::CloudFormation::WaitConditionHandle"
},
"WaitCondition" : {
"Type" : "AWS::CloudFormation::WaitCondition",
"DependsOn" : "VPNGateway",
"Properties" : {
"Handle" : { "Ref" : "WaitHandle" },
"Timeout" : "300"
}
},
"RouteA": {
"Type": "AWS::EC2::Route",
"DependsOn": ["VPNGateway"],
"Properties": {
"DestinationCidrBlock": {"Ref": "CidrBlock"},
"RouteTableId": {"Ref": "PrivateRouteTableA"},
"GatewayId": {"Ref": "VPNGateway"}
}
}
I have below 2 stacks
1) Stack 1 - this is network stack, defines vpc, subnets and security group
2) Stack 2 - this stack defines ec2 instance
Network stack exports following
WebServerSG:
Description : "Web Server Security Group"
Value: !GetAtt InstanceSecurityGroup.GroupId
Export:
Name: !Sub ${AWS::StackName}-WebServerSG
The ec2 instance stack accepts a parameter "NetworkStack" and uses the network stack to refer to the security group as follows
"Resources" : {
"WebServerInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"InstanceType" : { "Ref" : "InstanceType" },
"SubnetId" : {"Fn::ImportValue" : {"Fn::Sub" : "${NetworkStack}-SubnetADMZ"}},
"SecurityGroupIds" : {"Fn::ImportValue" : {"Fn::Sub" : "${NetworkStack}-WebServerSG"}},
"KeyName" : { "Ref" : "KeyName" }
}
}
},
The ec2 instance stack fails with the error , "Value of property SecurityGroupIds must be of type List of String"
I tried to use SecurityGroups instead , but received similar error
Even if you want to specify only 1 security group, CloudFormation wants a list. The solution here is to make a list of one element, the element being the imported security group. In your case it will look like this (notice the brackets):
...
"SecurityGroupIds" : [{"Fn::ImportValue" : {"Fn::Sub" : "${NetworkStack}-WebServerSG"}}],
...
Laurent Jalbert Simard answer is correct. Fn:ImportValue should be enclosed in [] to provide imported value as list.
I am writing a AWS Code formation. I have to print the Cidrblock of a subnet. But that does not work. Please help
"Resources": {
"Subnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "VPC"
},
"CidrBlock": "10.0.0.0/16",
}
},
Outputs : {
"SubnetCIDR": {
"Value": {
"Fn::GetAtt": [
"Subnet",
"CidrBlock"
]
},
"Description": "The CIDR"
},
}
This does not work. The following error message is shown while uploading the template:
Template validation error: Template error: resource Subnet does not
support attribute type CidrBlock in Fn::GetAtt
Not supported.
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html
If you look at the doc, the only supported attribute is AvailabilityZone
Since you seem to be hard coding the CIDR block anyway, you could set it as a parameter and then just reference the parameter in both places.
"Parameters" : {
"CidrBlock" : {
"Type" : "String",
"Default" : "10.0.0.0/16"
}
},
"Resources" : {
"Subnet" : {
"Type" : "AWS::EC2::Subnet",
"Properties" : {
"VpcId" : {
"Ref" : "VPC"
},
"CidrBlock" : { "Ref" : "CidrBlock" }
}
}
},
"Outputs" : {
"SubnetCIDR" : {
"Value" : { "Ref" : "CidrBlock" },
"Description": "The CIDR"
}
}
I have this AWS::EC2::SecurityGroup:
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable HTTP access on the configured port",
"VpcId" : { "Ref" : "VpcId" },
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : { "Ref" : "WebServerPort" },
"ToPort" : { "Ref" : "WebServerPort" },
"SourceSecurityGroupId" : { "Ref" : "LoadBalancerSecurityGroup" }
} ]
}
}
and I have this AWS::RDS::DBSecurityGroup
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"DBSecurityGroupIngress": { "EC2SecurityGroupName": { "Ref": "InstanceSecurityGroup"} },
"GroupDescription" : "Frontend Access"
}
}
when I try to bring up this stack, I get:
Invalid security group , groupId=, groupName= sg-a381fdc6.
Edit 1: Reading a bit more suggests I need AWS::RDS::DBSecurityGroup to be associated with my VPC, so I change to this:
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"EC2VpcId" : { "Ref" : "VpcId" },
"DBSecurityGroupIngress": { "EC2SecurityGroupName": { "Ref": "InstanceSecurityGroup"} },
"GroupDescription" : "Frontend Access"
}
}
and when I bring up the stack I get
Please see the documentation for authorizing DBSecurityGroup ingress. For VPC, EC2SecurityGroupId is required. To authorize only the source address of this request (and no other address), pass 205.251.233.35/32 as the CIDRIP parameter.
EC2SecurityGroupId is the ID of the security group, not the name of it, and that ID is assigned outside my control, so I don't know what value to put in here.
How do I connect my AWS::EC2::DBSecurityGroup to my AWS::RDS::DBSecurityGroup in a VPC context?
The problem is that your { "Ref": "InstanceSecurityGroup"} doesn't hold the id only the name. To get a hold on the EC2SecurityGroupId use Fn::GetAtt.
Your template for the DBSecurityGroup should look something like this (notice how Ref have been replaced by Fn::GetAtt:
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"EC2VpcId" : { "Ref" : "VpcId" },
"DBSecurityGroupIngress": { "EC2SecurityGroupId": { "Fn::GetAtt" : [ "InstanceSecurityGroup", "GroupId" ] } },
"GroupDescription" : "Frontend Access"
}
When you RDS Security group is defined inside a VPC, you must refer to other security group by group-id, not by group name.
See
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-security-group.html
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-security-group-rule.html
"For VPC DB Security Groups, use EC2SecurityGroupId. For EC2 Security Groups, use EC2SecurityGroupOwnerId and either EC2SecurityGroupName or EC2SecurityGroupId."
You can get the Security group ID by using the "Ref" function as described here
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html
So, your modified Security Group should be
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"EC2VpcId" : { "Ref" : "VpcId" },
"DBSecurityGroupIngress": { "EC2SecurityGroupId": { "Ref": "InstanceSecurityGroup"} },
"GroupDescription" : "Frontend Access"
}
}