This repository has been archived by the owner on Feb 7, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 108
/
template.yaml
310 lines (302 loc) · 11.4 KB
/
template.yaml
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
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS in Action: chapter 12'
Parameters:
KeyName:
Description: 'Key Pair name'
Type: 'AWS::EC2::KeyPair::KeyName'
Default: mykey
AdminEmailAddress:
Description: 'Email address of admin user'
Type: 'String'
Mappings:
RegionMap:
'ap-south-1':
AMI: 'ami-2ed19c41'
'eu-west-3':
AMI: 'ami-c8a017b5'
'eu-west-2':
AMI: 'ami-e3051987'
'eu-west-1':
AMI: 'ami-760aaa0f'
'ap-northeast-2':
AMI: 'ami-fc862292'
'ap-northeast-1':
AMI: 'ami-2803ac4e'
'sa-east-1':
AMI: 'ami-1678037a'
'ca-central-1':
AMI: 'ami-ef3b838b'
'ap-southeast-1':
AMI: 'ami-dd7935be'
'ap-southeast-2':
AMI: 'ami-1a668878'
'eu-central-1':
AMI: 'ami-e28d098d'
'us-east-1':
AMI: 'ami-6057e21a'
'us-east-2':
AMI: 'ami-aa1b34cf'
'us-west-1':
AMI: 'ami-1a033c7a'
'us-west-2':
AMI: 'ami-32d8124a'
Resources:
##########################################################################
# #
# VPC with two public subnets #
# #
##########################################################################
VPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: '172.31.0.0/16'
EnableDnsHostnames: true
InternetGateway:
Type: 'AWS::EC2::InternetGateway'
Properties: {}
VPCGatewayAttachment:
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
SubnetA:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: '172.31.38.0/24'
VpcId: !Ref VPC
SubnetB:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: '172.31.37.0/24'
VpcId: !Ref VPC
RouteTable:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref VPC
SubnetRouteTableAssociationA:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetA
RouteTableId: !Ref RouteTable
SubnetRouteTableAssociationB:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetB
RouteTableId: !Ref RouteTable
RouteToInternet:
Type: 'AWS::EC2::Route'
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
DependsOn: VPCGatewayAttachment
NetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref VPC
SubnetNetworkAclAssociationA:
Type: 'AWS::EC2::SubnetNetworkAclAssociation'
Properties:
SubnetId: !Ref SubnetA
NetworkAclId: !Ref NetworkAcl
SubnetNetworkAclAssociationB:
Type: 'AWS::EC2::SubnetNetworkAclAssociation'
Properties:
SubnetId: !Ref SubnetB
NetworkAclId: !Ref NetworkAcl
NetworkAclEntryIngress:
Type: 'AWS::EC2::NetworkAclEntry'
Properties:
NetworkAclId: !Ref NetworkAcl
RuleNumber: 100
Protocol: -1
RuleAction: allow
Egress: false
CidrBlock: '0.0.0.0/0'
NetworkAclEntryEgress:
Type: 'AWS::EC2::NetworkAclEntry'
Properties:
NetworkAclId: !Ref NetworkAcl
RuleNumber: 100
Protocol: -1
RuleAction: allow
Egress: true
CidrBlock: '0.0.0.0/0'
##########################################################################
# #
# Cache #
# #
##########################################################################
CacheSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: cache
VpcId: !Ref VPC
CacheSecurityGroupIngress:
Type: 'AWS::EC2::SecurityGroupIngress'
Properties:
GroupId: !Ref CacheSecurityGroup
IpProtocol: tcp
FromPort: 6379
ToPort: 6379
SourceSecurityGroupId: !Ref VMSecurityGroup
Cache:
Type: 'AWS::ElastiCache::CacheCluster'
Properties:
CacheNodeType: 'cache.t2.micro'
CacheSubnetGroupName: !Ref CacheSubnetGroup
Engine: redis
EngineVersion: '3.2.4'
NumCacheNodes: 1
VpcSecurityGroupIds:
- !Ref CacheSecurityGroup
CacheSubnetGroup:
Type: 'AWS::ElastiCache::SubnetGroup'
Properties:
Description: cache
SubnetIds:
- Ref: SubnetA
- Ref: SubnetB
##########################################################################
# #
# Database #
# #
##########################################################################
DatabaseSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: database
VpcId: !Ref VPC
DatabaseSecurityGroupIngress:
Type: 'AWS::EC2::SecurityGroupIngress'
Properties:
GroupId: !Ref DatabaseSecurityGroup
IpProtocol: tcp
FromPort: 5432
ToPort: 5432
SourceSecurityGroupId: !Ref VMSecurityGroup
Database:
Type: 'AWS::RDS::DBInstance'
DeletionPolicy: Delete # For AWS::RDS::DBInstance resources that don't specify the DBClusterIdentifier property, the default policy is Snapshot which can cause unwanted costs. However, for production setups, we highly recommend to stay with the default to avoid data loss.
Properties:
AllocatedStorage: '5'
BackupRetentionPeriod: 0
DBInstanceClass: 'db.t2.micro'
DBName: discourse
Engine: postgres
EngineVersion: '9.6.22'
MasterUsername: discourse
MasterUserPassword: discourse
VPCSecurityGroups:
- !Sub ${DatabaseSecurityGroup.GroupId}
DBSubnetGroupName: !Ref DatabaseSubnetGroup
DependsOn: VPCGatewayAttachment
DatabaseSubnetGroup:
Type: 'AWS::RDS::DBSubnetGroup'
Properties:
DBSubnetGroupDescription: database
SubnetIds:
- Ref: SubnetA
- Ref: SubnetB
##########################################################################
# #
# Virtual machine #
# #
##########################################################################
VMSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: 'vm'
SecurityGroupIngress:
- CidrIp: '0.0.0.0/0'
FromPort: 22
IpProtocol: tcp
ToPort: 22
- CidrIp: '0.0.0.0/0'
FromPort: 80
IpProtocol: tcp
ToPort: 80
VpcId: !Ref VPC
VMInstance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', AMI]
InstanceType: 't2.micro'
KeyName: !Ref KeyName
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeleteOnTermination: true
DeviceIndex: '0'
GroupSet:
- !Ref VMSecurityGroup
SubnetId: !Ref SubnetA
UserData:
'Fn::Base64': !Sub |
#!/bin/bash -x
bash -ex << "TRY"
# t2.micro may not have enough memory, so we add a 512MB swapfile
dd if=/dev/zero of=/swapfile1 bs=1024 count=524288
chmod 0600 /swapfile1
mkswap /swapfile1
swapon /swapfile1
echo "/swapfile1 none swap sw 0 0" >> /etc/fstab
# get public IP address of this EC2 instance
PUBLICIP="$(curl -s -m 60 http://169.254.169.254/latest/meta-data/public-ipv4)"
# install packages
yum -y group install "Development Tools"
yum -y install git zlib-devel postgresql96-devel libyaml-devel ImageMagick ruby23-devel nginx
yum -y install --enablerepo=epel optipng jhead jpegoptim gifsicle libjpeg-turbo-utils
update-alternatives --set ruby /usr/bin/ruby2.3
gem install bundler -v "< 2.0, >= 1.3.0" --no-ri --no-rdoc
# download Discourse
useradd discourse
mkdir /opt/discourse
git clone https://github.com/AWSinAction/discourse.git /opt/discourse
# configure Discourse
echo "gem 'psych', '~> 3.1'" >> /opt/discourse/Gemfile
echo "gem 'json', '~> 2.3'" >> /opt/discourse/Gemfile
echo "gem 'bigdecimal', '~> 1.4', '>= 1.4.4'" >> /opt/discourse/Gemfile
echo "db_host = \"${Database.Endpoint.Address}\"" > /opt/discourse/config/discourse.conf
echo "db_password = \"discourse\"" >> /opt/discourse/config/discourse.conf
echo "redis_host = \"${Cache.RedisEndpoint.Address}\"" >> /opt/discourse/config/discourse.conf
echo "hostname = \"$PUBLICIP\"" >> /opt/discourse/config/discourse.conf
echo "serve_static_assets = true" >> /opt/discourse/config/discourse.conf
echo "developer_emails = \"${AdminEmailAddress}\"" >> /opt/discourse/config/discourse.conf
echo "notification_email = \"${AdminEmailAddress}\"" >> /opt/discourse/config/discourse.conf
echo "smtp_address = \"localhost\"" >> /opt/discourse/config/discourse.conf
echo "smtp_port = 25" >> /opt/discourse/config/discourse.conf
echo "smtp_enable_start_tls = false" >> /opt/discourse/config/discourse.conf
echo "smtp_openssl_verify_mode = none" >> /opt/discourse/config/discourse.conf
sed -i 's/development:/production:/g' /opt/discourse/config/sidekiq.yml
chown -R discourse:discourse /opt/discourse
# prepare Discourse
runuser -l discourse -c 'cd /opt/discourse && bundle install'
runuser -l discourse -c 'cd /opt/discourse && RAILS_ENV=production bundle exec rake db:migrate'
runuser -l discourse -c 'cd /opt/discourse && RAILS_ENV=production bundle exec rake assets:precompile'
# start Discourse
runuser -l discourse -c 'cd /opt/discourse && RAILS_ENV=production bundle exec sidekiq -L log/sidekiq.log -e production -d'
runuser -l discourse -c 'cd /opt/discourse && RAILS_ENV=production bundle exec rails s -d'
# configure Nginx to forward port 80 to 3000
echo "server {" > /etc/nginx/conf.d/discourse.conf
echo " listen 80;" >> /etc/nginx/conf.d/discourse.conf
echo " server_name $PUBLICIP;" >> /etc/nginx/conf.d/discourse.conf
echo " location / {" >> /etc/nginx/conf.d/discourse.conf
echo " proxy_pass http://127.0.0.1:3000;" >> /etc/nginx/conf.d/discourse.conf
echo " }" >> /etc/nginx/conf.d/discourse.conf
echo "}" >> /etc/nginx/conf.d/discourse.conf
# start Nginx
service nginx start
TRY
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource VMInstance --region ${AWS::Region}
CreationPolicy:
ResourceSignal:
Timeout: PT15M
DependsOn:
- VPCGatewayAttachment
Outputs:
VMInstanceIPAddress:
Value: !GetAtt 'VMInstance.PublicIp'
Description: 'EC2 Instance public IP address (connect via SSH as user ec2-user)'