Skip to content

Commit 70d2b1a

Browse files
authored
Merge branch 'develop' into s3-vpc-endpoint
2 parents 5ff938b + 4a61205 commit 70d2b1a

File tree

7 files changed

+129
-23
lines changed

7 files changed

+129
-23
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Change Log
2020
What's new in 2.0.0:
2121

2222
* Re-purpose use_aes256_encryption flag to support encryption across S3, RDS, Elasticache (Redis only), and RDS (thanks @dsummersl)
23-
* Add support for Customer Managed CMKs with ``CustomerManagedCmkArn`` parameter
23+
* Add support for Customer Managed CMKs with ``CustomerManagedCmkArn`` parameter (not applied to public buckets)
2424
* Add configurable ContainerVolumeSize to change root volume size of EC2 instances (thanks @dsummersl)
2525
* Change generated template output from JSON to YAML (thanks @cchurch)
2626
* Add required DBParameterGroup by default, which allows configuring database specific parameters. This avoids having to reboot a production database instance to add a DBParameterGroup in the future. (thanks @cchurch)
@@ -34,6 +34,7 @@ What's new in 2.0.0:
3434
* Add RDS and ElastiCache endpoint outputs.
3535
* Add CustomAppCertificateArn parameter to allow association with an existing ACM certificate.
3636
* Add VPC Endpoint for S3.
37+
* Add DatabaseReplication parameter to add a database replica (** this will fail if DatabaseBackupRetentionDays is 0.**).
3738

3839

3940
`1.4.0`_ (2019-08-05)

README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ These environment variables are:
310310
* ``DATABASE_URL``: The URL to the RDS instance created as part of this stack. If ``(none)`` is
311311
selected for the ``DatabaseClass`` during stack creation, the value of this variable will be
312312
an empty string (``''``).
313+
* ``DATABASE_REPLICA_URL``: The URL to the RDS database replica instance. This is an empty string
314+
if there's no replica database.
313315
* ``CACHE_URL``: The URL to the Redis or Memcached instance created as part of this stack (may be
314316
used as a cache or session storage, e.g.). If using Redis, note that it supports multiple
315317
databases and no database ID is included as part of the URL, so you should append a forward slash

stack/assets.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,6 @@
6464
)
6565

6666
common_bucket_conf = dict(
67-
BucketEncryption=BucketEncryption(
68-
ServerSideEncryptionConfiguration=If(
69-
use_aes256_encryption_cond,
70-
[
71-
ServerSideEncryptionRule(
72-
ServerSideEncryptionByDefault=ServerSideEncryptionByDefault(
73-
SSEAlgorithm=If(use_cmk_arn, 'aws:kms', 'AES256'),
74-
KMSMasterKeyID=If(use_cmk_arn, Ref(cmk_arn), Ref("AWS::NoValue")),
75-
)
76-
)
77-
],
78-
[
79-
ServerSideEncryptionRule()
80-
]
81-
)
82-
),
8367
VersioningConfiguration=VersioningConfiguration(
8468
Status="Enabled"
8569
),
@@ -118,6 +102,21 @@
118102
Bucket(
119103
"AssetsBucket",
120104
AccessControl=Ref(assets_bucket_access_control),
105+
BucketEncryption=BucketEncryption(
106+
ServerSideEncryptionConfiguration=If(
107+
use_aes256_encryption_cond,
108+
[
109+
ServerSideEncryptionRule(
110+
ServerSideEncryptionByDefault=ServerSideEncryptionByDefault(
111+
SSEAlgorithm='AES256'
112+
)
113+
)
114+
],
115+
[
116+
ServerSideEncryptionRule()
117+
]
118+
)
119+
),
121120
**common_bucket_conf,
122121
)
123122
)
@@ -142,6 +141,22 @@
142141
IgnorePublicAcls=True,
143142
RestrictPublicBuckets=True,
144143
),
144+
BucketEncryption=BucketEncryption(
145+
ServerSideEncryptionConfiguration=If(
146+
use_aes256_encryption_cond,
147+
[
148+
ServerSideEncryptionRule(
149+
ServerSideEncryptionByDefault=ServerSideEncryptionByDefault(
150+
SSEAlgorithm=If(use_cmk_arn, 'aws:kms', 'AES256'),
151+
KMSMasterKeyID=If(use_cmk_arn, Ref(cmk_arn), Ref("AWS::NoValue")),
152+
)
153+
)
154+
],
155+
[
156+
ServerSideEncryptionRule()
157+
]
158+
)
159+
),
145160
**common_bucket_conf,
146161
)
147162
)
@@ -304,7 +319,7 @@
304319
# Output CloudFront url
305320
template.add_output(Output(
306321
"AssetsDistributionDomainName",
307-
Description="The assest CDN domain name",
322+
Description="The assets CDN domain name",
308323
Value=GetAtt(distribution, "DomainName"),
309324
Condition=assets_use_cloudfront_condition,
310325
))

stack/bastion.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Condition,
55
Equals,
66
FindInMap,
7+
GetAtt,
78
If,
89
Join,
910
Not,
@@ -43,8 +44,8 @@
4344
Parameter(
4445
"BastionAMI",
4546
Description="(Optional) Bastion or VPN server AMI in the same region as this stack.",
46-
Type="AWS::EC2::Image::Id",
47-
Default=dont_create_value,
47+
Type="String",
48+
Default="",
4849
),
4950
group="Bastion Server",
5051
label="AMI",
@@ -181,7 +182,7 @@
181182
template.add_condition(bastion_type_is_ssh_set, Equals("SSH", Ref(bastion_type)))
182183

183184
bastion_ami_set = "BastionAMISet"
184-
template.add_condition(bastion_ami_set, Not(Equals(dont_create_value, Ref(bastion_ami))))
185+
template.add_condition(bastion_ami_set, Not(Equals("", Ref(bastion_ami))))
185186

186187
bastion_type_and_ami_set = "BastionTypeAndAMISet"
187188
template.add_condition(bastion_type_and_ami_set, And(Condition(bastion_type_set), Condition(bastion_ami_set)))
@@ -279,6 +280,7 @@
279280
"BastionEIP",
280281
template=template,
281282
Condition=bastion_type_set,
283+
Domain="vpc",
282284
)
283285

284286
bastion_instance = ec2.Instance(
@@ -318,7 +320,7 @@
318320
"BastionEIPAssociation",
319321
template=template,
320322
InstanceId=Ref(bastion_instance),
321-
EIP=Ref(bastion_eip),
323+
AllocationId=GetAtt(bastion_eip, "AllocationId"),
322324
Condition=bastion_type_and_ami_set,
323325
)
324326

stack/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
cmk_arn = template.add_parameter(
159159
Parameter(
160160
"CustomerManagedCmkArn",
161-
Description="KMS CMK ARN to encrypt stack resources.",
161+
Description="KMS CMK ARN to encrypt stack resources (except for public buckets).",
162162
Type="String",
163163
Default="",
164164
),

stack/database.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from collections import OrderedDict
22

33
from troposphere import (
4+
And,
5+
Condition,
46
Equals,
57
FindInMap,
68
GetAtt,
@@ -97,6 +99,27 @@
9799
db_condition = "DatabaseCondition"
98100
template.add_condition(db_condition, Not(Equals(Ref(db_class), dont_create_value)))
99101

102+
db_replication = template.add_parameter(
103+
Parameter(
104+
"DatabaseReplication",
105+
Type="String",
106+
AllowedValues=["true", "false"],
107+
Default="false",
108+
Description="Whether to create a database server replica - "
109+
"WARNING this will fail if DatabaseBackupRetentionDays is 0.",
110+
),
111+
group="Database",
112+
label="Database replication"
113+
)
114+
db_replication_condition = "DatabaseReplicationCondition"
115+
template.add_condition(
116+
db_replication_condition,
117+
And(
118+
Condition(db_condition),
119+
Equals(Ref(db_replication), "true")
120+
)
121+
)
122+
100123
db_engine = template.add_parameter(
101124
Parameter(
102125
"DatabaseEngine",
@@ -361,6 +384,16 @@
361384
KmsKeyId=If(use_cmk_arn, Ref(cmk_arn), Ref("AWS::NoValue")),
362385
)
363386

387+
db_replica = rds.DBInstance(
388+
"DatabaseReplica",
389+
template=template,
390+
Condition=db_replication_condition,
391+
SourceDBInstanceIdentifier=Ref(db_instance),
392+
DBInstanceClass=Ref(db_class),
393+
Engine=Ref(db_engine),
394+
VPCSecurityGroups=[Ref(db_security_group)],
395+
)
396+
364397
db_url = If(
365398
db_condition,
366399
Join("", [
@@ -377,6 +410,22 @@
377410
"", # defaults to empty string if no DB was created
378411
)
379412

413+
db_replica_url = If(
414+
db_replication_condition,
415+
Join("", [
416+
Ref(db_engine),
417+
"://",
418+
Ref(db_user),
419+
":_PASSWORD_@",
420+
GetAtt(db_replica, 'Endpoint.Address'),
421+
":",
422+
GetAtt(db_replica, 'Endpoint.Port'),
423+
"/",
424+
Ref(db_name),
425+
]),
426+
"", # defaults to empty string if no DB was created
427+
)
428+
380429
template.add_output([
381430
Output(
382431
"DatabaseURL",
@@ -386,6 +435,15 @@
386435
),
387436
])
388437

438+
template.add_output([
439+
Output(
440+
"DatabaseReplicaURL",
441+
Description="URL to connect (without the password) to the database replica.",
442+
Value=db_replica_url,
443+
Condition=db_replication_condition,
444+
),
445+
])
446+
389447
template.add_output([
390448
Output(
391449
"DatabasePort",
@@ -403,3 +461,12 @@
403461
Condition=db_condition,
404462
),
405463
])
464+
465+
template.add_output([
466+
Output(
467+
"DatabaseReplicaAddress",
468+
Description="The connection endpoint for the database replica.",
469+
Value=GetAtt(db_replica, "Endpoint.Address"),
470+
Condition=db_replication_condition
471+
),
472+
])

stack/environment.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
db_instance,
1919
db_name,
2020
db_password,
21+
db_replica,
22+
db_replication_condition,
2123
db_user
2224
)
2325
from .domain import domain_name, domain_name_alternates
@@ -53,6 +55,23 @@
5355
]),
5456
"", # defaults to empty string if no DB was created
5557
)),
58+
("DATABASE_REPLICA_URL", If(
59+
db_replication_condition,
60+
Join("", [
61+
Ref(db_engine),
62+
"://",
63+
Ref(db_user),
64+
":",
65+
Ref(db_password),
66+
"@",
67+
GetAtt(db_replica, 'Endpoint.Address'),
68+
":",
69+
GetAtt(db_replica, 'Endpoint.Port'),
70+
"/",
71+
Ref(db_name),
72+
]),
73+
"", # defaults to empty string if no DB was created
74+
)),
5675
("CACHE_URL", cache_url),
5776
("REDIS_URL", redis_url),
5877
]

0 commit comments

Comments
 (0)