in src/ModernTacoShop/Common/cdk/CommonStack.cs [15:233]
internal ModernTacoShopStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var publicHostedZoneDomainName = (string)this.Node.TryGetContext("domain-name");
// Create a VPC.
var vpc = new Vpc(this, "vpc", new VpcProps
{
Cidr = "192.168.123.0/24",
MaxAzs = 3,
SubnetConfiguration = new SubnetConfiguration[]
{
new SubnetConfiguration
{
CidrMask = 27,
SubnetType = SubnetType.PUBLIC,
Name = "Public",
},
new SubnetConfiguration
{
CidrMask = 27,
SubnetType = SubnetType.PRIVATE,
Name = "Private"
}
}
});
// Security: Turn off `MapPublicIpOnLaunch` for public subnets (where the CDK construct enables it by default).
foreach (var subnet in vpc.PublicSubnets)
{
var cfnSubnet = (CfnSubnet)subnet.Node.DefaultChild;
cfnSubnet.MapPublicIpOnLaunch = false;
}
// Security: Suppress the cfn_nag warning for VPC flow logs.
((CfnVPC)vpc.Node.DefaultChild).AddMetadata("cfn_nag",
new Dictionary<string, object>
{
["rules_to_suppress"] = new Dictionary<string, object>[]
{
new Dictionary<string, object>
{
["id"] = "W60",
["reason"] = "VPC flow logs not required in this sample."
}
}
});
// Create an S3 bucket to hold the uploaded code.
var codeBucket = new Bucket(this,
"ModernTacoShop-CodeBucket",
new BucketProps()
{
AccessControl = BucketAccessControl.LOG_DELIVERY_WRITE,
Encryption = BucketEncryption.S3_MANAGED,
ServerAccessLogsPrefix = "access-logs/modern-taco-shop/"
});
// Allow ELB logs access to the bucket.
// See: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-logging-bucket-permissions
var loadBalancerAccessLogPrefix = "access-logs/modern-taco-shop/*";
var loadBalancerAccessLogResource = $"arn:aws:s3:::{codeBucket.BucketName}/{loadBalancerAccessLogPrefix}/AWSLogs/{this.Account}/*";
// The ELB account ID is different per region, and there's no API to get it.
var regionalElbAccountIds = new Dictionary<string, string> {
{ "ap-east-1", "754344448648" },
{ "ap-northeast-1", "582318560864" },
{ "ap-northeast-2", "600734575887" },
{ "ap-northeast-3", "383597477331" },
{ "ap-south-1", "718504428378" },
{ "ap-southeast-1", "114774131450" },
{ "ap-southeast-2", "783225319266" },
{ "ca-central-1", "985666609251" },
{ "cn-north-1", "638102146993" },
{ "cn-northwest-1", "037604701340" },
{ "eu-central-1", "054676820928" },
{ "eu-north-1", "897822967062" },
{ "eu-west-1", "156460612806" },
{ "eu-west-2", "652711504416" },
{ "eu-west-3", "009996457667" },
{ "sa-east-1", "507241528517" },
{ "us-east-1", "127311923021" },
{ "us-east-2", "033677994240" },
{ "us-gov-east-1", "190560391635" },
{ "us-gov-west-1", "048591011584" },
{ "us-west-1", "027434742980" },
{ "us-west-2", "797873946194" },
};
var elbAccountId = regionalElbAccountIds[this.Region];
codeBucket.AddToResourcePolicy(new PolicyStatement(
new PolicyStatementProps()
{
Actions = new[] { "s3:PutObject" },
Effect = Effect.ALLOW,
Resources = new[] { loadBalancerAccessLogResource },
Principals = new[] { new AccountPrincipal(elbAccountId) }
}));
codeBucket.AddToResourcePolicy(new PolicyStatement(
new PolicyStatementProps()
{
Actions = new[] { "s3:PutObject" },
Effect = Effect.ALLOW,
Resources = new[] { loadBalancerAccessLogResource },
Principals = new[] { new ServicePrincipal("delivery.logs.amazonaws.com") },
Conditions = new Dictionary<string, object>
{
{ "StringEquals", new Dictionary<string, object> { { "s3:x-amz-acl", "bucket-owner-full-control" } } }
}
}));
codeBucket.AddToResourcePolicy(new PolicyStatement(
new PolicyStatementProps()
{
Actions = new[] { "s3:GetBucketAcl" },
Effect = Effect.ALLOW,
Resources = new[] { codeBucket.BucketArn },
Principals = new[] { new ServicePrincipal("delivery.logs.amazonaws.com") },
}));
// Store the name of the code bucket in Systems Manager so we can
// refer to it in other service scripts.
var codeBucketNameParameter = new StringParameter(this,
"ModernTacoShop-CodeBucketNameParameter",
new StringParameterProps
{
ParameterName = "/ModernTacoShop/CodeBucketName",
StringValue = codeBucket.BucketName,
Tier = ParameterTier.STANDARD
}
);
new CfnOutput(this, "ModernTacoShop-CodeBucketNameOutput", new CfnOutputProps
{
Value = codeBucket.BucketName
});
// Get the Route 53 hosted zone that will serve as a 'parent' for the microservices.
var hostedZone = HostedZone.FromLookup(this,
"ModernTacoShop-ParentHostedZone",
new HostedZoneProviderProps()
{
DomainName = publicHostedZoneDomainName
});
// Store the name and ID of the hosted zone in Systems Manager, so we can refer to it in other service scripts.
new StringParameter(this,
"ModernTacoShop-HostedZoneNameParameter",
new StringParameterProps
{
ParameterName = "/ModernTacoShop/HostedZoneName",
StringValue = hostedZone.ZoneName,
Tier = ParameterTier.STANDARD
});
new StringParameter(this,
"ModernTacoShop-HostedZoneIdParameter",
new StringParameterProps
{
ParameterName = "/ModernTacoShop/HostedZoneID",
StringValue = hostedZone.HostedZoneId,
Tier = ParameterTier.STANDARD
});
// Register a certificate for the hosted zone.
var certificate = new Certificate(this,
"ModernTacoShop-Certificate",
new CertificateProps
{
DomainName = hostedZone.ZoneName,
SubjectAlternativeNames = new[] { "*." + hostedZone.ZoneName },
Validation = CertificateValidation.FromDns(hostedZone)
});
// Store the certificate ARN in Systems Manager, so we can refer to it in other service scripts.
new StringParameter(this,
"ModernTacoShop-CertificateArnParameter",
new StringParameterProps
{
ParameterName = "/ModernTacoShop/CertificateARN",
StringValue = certificate.CertificateArn,
Tier = ParameterTier.STANDARD
});
// Create a policy giving access to SSM parameters that contain microservice domain names.
var policy = new ManagedPolicy(this,
"ModernTacoShop-ReadMicroserviceDomainNameParametersPolicy",
new ManagedPolicyProps()
{
Description = "Allows read-only access to SSM parameters tagged as microservice domain names."
});
policy.AddStatements(
new PolicyStatement[]
{
new PolicyStatement(new PolicyStatementProps
{
Actions = new string[] { "ssm:GetParameter" },
Effect = Effect.ALLOW,
Resources = new string[] { $"arn:aws:ssm:{this.Region}:{this.Account}:parameter/*" },
Conditions = new Dictionary<string, object>
{
["StringEquals"] = new Dictionary<string, string>
{
["aws:ResourceTag/parameter-type"] = "modern-taco-shop-microservice-domain-name"
}
}
})
}
);
// Store the policy ARN in Systems Manager, so we can refer to it in other service scripts.
new StringParameter(this,
"ModernTacoShop-ReadMicroserviceDomainNameParametersPolicyArnParameter",
new StringParameterProps
{
ParameterName = "/ModernTacoShop/ReadMicroserviceDomainNameParametersPolicyARN",
StringValue = policy.ManagedPolicyArn,
Tier = ParameterTier.STANDARD
});
}