-
Notifications
You must be signed in to change notification settings - Fork 101
/
base-vpc-example-improved.template.yml
366 lines (342 loc) · 12.3 KB
/
base-vpc-example-improved.template.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
AWSTemplateFormatVersion: 2010-09-09
# This CloudFormation template deploys a basic VPC / Network. Specifically:
# It deploys a VPC with 1 to 6 subnets 3 Availability Zones depending on parameter selection.
# User has the choice of public subnets or private + public subnets in each AZ.
# When private subnets are created, NATing is provided by either an EC2 instance or NAT Gateway based on user selection.
# The VPC and subnets are exported for use by other stacks.
Metadata:
# Control the UI display when running this template from the AWS Management Console:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Network Configuration"
Parameters:
- NumberOfAZs
- PrivateSubnets
- NATType
Parameters:
PrivateSubnets:
Type: String
AllowedValues:
- True
- False
Default: True
Description: Do you want to create private subnets in addition to public subnets?
NumberOfAZs:
Type: Number
AllowedValues:
- 1
- 2
- 3
Default: 2
Description: How many Availability Zones do you wish to utilize?
NATType:
Type: String
AllowedValues:
- "EC2 NAT Instance"
- "NAT Gateway"
Default: "NAT Gateway"
Description: What type of NAT to use for private instances to communicate with the internet. A single EC2 instance can be used as a NAT, or you can use the AWS NAT Gateway (managed, scalable, more expensive). This setting will be IGNORED if you do not build private subnets.
Conditions:
BuildPublicB: !Not [ !Equals [ !Ref NumberOfAZs, 1 ]]
BuildPublicC: !Equals [ !Ref NumberOfAZs, 3 ]
BuildPrivateSubnets: !Equals [ !Ref PrivateSubnets, True ]
BuildPrivateA: !Equals [ !Ref PrivateSubnets, True ] # Can't determine how to build a condition from a separate single condition
BuildPrivateB: !And [ Condition: BuildPrivateSubnets, Condition: BuildPublicB ]
BuildPrivateC: !And [ Condition: BuildPrivateSubnets, Condition: BuildPublicC ]
BuildNATGateway: !And [ Condition: BuildPrivateSubnets, !Equals [ !Ref NATType, "NAT Gateway" ] ]
BuildNATInstance: !And [ Condition: BuildPrivateSubnets, !Equals [ !Ref NATType, "EC2 NAT Instance" ] ]
Mappings:
# This is the Amazon Linux 2 AMI. Adjust these values as needed, they can change a few times per year:
AmazonLinuxAMI:
us-east-1:
AMI: ami-04681a1dbd79675a5 # N Virginia
us-east-2:
AMI: ami-0cf31d971a3ca20d6 # Ohio
us-west-1:
AMI: ami-0782017a917e973e7 # N California
us-west-2:
AMI: ami-6cd6f714 # Oregon
eu-west-1:
AMI: ami-0bdb1d6c15a40392c # Ireland
eu-central-1:
AMI: ami-0f5dbc86dd9cbf7a8 # Frankfurt
sa-east-1:
AMI: ami-0ad7b0031d41ed4b9 # Sao Paulo
ap-southeast-1:
AMI: ami-01da99628f381e50a # Singaport
ap-southeast-2:
AMI: ami-00e17d1165b9dd3ec # Sydney
ap-northeast-1:
AMI: ami-08847abae18baa040 # Tokyo
Resources:
# First, a VPC:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.1.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-VPC
# Our VPC will need internet access:
InternetGateway:
Type: AWS::EC2::InternetGateway
DependsOn: VPC
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
# Notice how you can't attach an IGW to a VPC unless both are created:
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# Now some subnets, 2 public and 2 private:
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.10.0/24
AvailabilityZone: !Select [ 0, !GetAZs ] # Get the first AZ in the list
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Public-A
PublicSubnetB:
Type: AWS::EC2::Subnet
Condition: BuildPublicB
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.20.0/24
AvailabilityZone: !Select [ 1, !GetAZs ] # Get the second AZ in the list
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Public-B
PublicSubnetC:
Type: AWS::EC2::Subnet
Condition: BuildPublicC
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.30.0/24
AvailabilityZone: !Select [ 2, !GetAZs ] # Get the third AZ in the list
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Public-C
PrivateSubnetA:
Type: AWS::EC2::Subnet
Condition: BuildPrivateA
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.50.0/24
AvailabilityZone: !Select [ 0, !GetAZs ] # Get the first AZ in the list
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Private-A
PrivateSubnetB:
Type: AWS::EC2::Subnet
Condition: BuildPrivateB
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.60.0/24
AvailabilityZone: !Select [ 1, !GetAZs ] # Get the second AZ in the list
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Private-B
PrivateSubnetC:
Type: AWS::EC2::Subnet
Condition: BuildPrivateC
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.70.0/24
AvailabilityZone: !Select [ 2, !GetAZs ] # Get the third AZ in the list
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-Private-C
# Some route tables for our subnets:
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Public
PublicRoute1: # Public route table has direct routing to IGW:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Here is a private route table:
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Condition: BuildPrivateSubnets
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Private
PrivateRoute1: # Private route table can access web via NAT (created below)
Type: AWS::EC2::Route
Condition: BuildPrivateSubnets
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
# If we are using a NAT Instance, route traffic through the NAT Instance:
InstanceId: !If [ BuildNATInstance, !Ref NATInstance, !Ref "AWS::NoValue" ]
# Otherwise if we are using a NAT Gateway, route traffic through the NAT Gateway:
NatGatewayId: !If [ BuildNATGateway, !Ref NATGateway, !Ref "AWS::NoValue" ]
# Attach the public subnets to public route tables,
# and attach the private subnets to private route tables:
PublicSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTable
PublicSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: BuildPublicB
Properties:
SubnetId: !Ref PublicSubnetB
RouteTableId: !Ref PublicRouteTable
PublicSubnetCRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: BuildPublicC
Properties:
SubnetId: !Ref PublicSubnetC
RouteTableId: !Ref PublicRouteTable
PrivateSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: BuildPrivateA
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: BuildPrivateB
Properties:
SubnetId: !Ref PrivateSubnetB
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetCRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: BuildPrivateC
Properties:
SubnetId: !Ref PrivateSubnetC
RouteTableId: !Ref PrivateRouteTable
# A security group for our NAT. Ingress from the VPC IPs only. Egress is TCP & UDP only:
NATSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: BuildNATInstance
DependsOn: AttachGateway
Properties:
GroupName: !Sub NATSecurityGroup-${AWS::StackName}
GroupDescription: Enable internal access to the NAT device
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '0'
ToPort: '1024'
CidrIp: !GetAtt VPC.CidrBlock
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: '0'
ToPort: '65535'
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: '0'
ToPort: '65535'
CidrIp: 0.0.0.0/0
# A NAT Instance will be built and used if the user selected Private subnets and specified an EC2 instance over a managed Gateway.
NATInstance:
Type: AWS::EC2::Instance
Condition: BuildNATInstance
DependsOn: PublicRoute1 # Must have route to IGW established.
Properties:
ImageId: !FindInMap [ AmazonLinuxAMI, !Ref "AWS::Region", AMI] # We lookup the AMI from the Region Map
InstanceType: t2.small # Any instance type is fine
NetworkInterfaces:
- DeviceIndex: '0'
SubnetId: !Ref PublicSubnetA # Any public subnet is fine
AssociatePublicIpAddress: true # We will definitely need a public IP address
GroupSet: [!Ref NATSecurityGroup] # Plug in the security group
SourceDestCheck: false # NATs don't work if EC2 tries the match source addresses with destinations.
Tags:
- Key: Name
Value: !Sub NAT-${AWS::StackName}
UserData: # This code is NAT code. Look at how the last line signals the wait condition:
Fn::Base64: !Sub |
#!/bin/bash
yum -y update
yum install -y aws-cfn-bootstrap
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv4/conf/eth0/send_redirects
/sbin/iptables -t nat -A POSTROUTING -o eth0 -s 0.0.0.0/0 -j MASQUERADE
/sbin/iptables-save > /etc/sysconfig/iptables
mkdir -p /etc/sysctl.d/
cat << NatConfFileMarker > /etc/sysctl.d/nat.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.eth0.send_redirects = 0
NatConfFileMarker
/opt/aws/bin/cfn-signal -e 0 --resource NATInstance --stack ${AWS::StackName} --region ${AWS::Region}
# This NATInstance is only complete when you get 1 signal back within 5 minutes'.
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT5M
# A NAT Gateway will be built and used if the user selected Private subnets and a Gateway instead of an EC2 instance.
NATGateway:
Type: AWS::EC2::NatGateway
Condition: BuildNATGateway
Properties:
AllocationId: !GetAtt ElasticIPAddress.AllocationId
SubnetId: !Ref PublicSubnetA
Tags:
- Key: Name
Value: !Sub NAT-${AWS::StackName}
ElasticIPAddress:
Type: AWS::EC2::EIP
Condition: BuildNATGateway
Properties:
Domain: VPC
Outputs:
VPC:
Description: VPC of the base network
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-VPC
PublicSubnetA:
Description: First Public Subnet
Value: !Ref PublicSubnetA
Export:
Name: !Sub ${AWS::StackName}-PublicSubnetA
PublicSubnetB:
Description: Second Public Subnet
Condition: BuildPublicB
Value: !Ref PublicSubnetB
Export:
Name: !Sub ${AWS::StackName}-PublicSubnetB
PublicSubnetC:
Description: Third Public Subnet
Condition: BuildPublicC
Value: !Ref PublicSubnetC
Export:
Name: !Sub ${AWS::StackName}-PublicSubnetC
PrivateSubnetA:
Condition: BuildPrivateSubnets
Description: First Private Subnet
Value: !Ref PrivateSubnetA
Export:
Name: !Sub ${AWS::StackName}-PrivateSubnetA
PrivateSubnetB:
Condition: BuildPrivateB
Description: Second Private Subnet
Value: !Ref PrivateSubnetB
Export:
Name: !Sub ${AWS::StackName}-PrivateSubnetB
PrivateSubnetC:
Condition: BuildPrivateC
Description: Third Private Subnet
Value: !Ref PrivateSubnetC
Export:
Name: !Sub ${AWS::StackName}-PrivateSubnetC