├── LICENSE ├── README.md ├── diagram1.png ├── diagram2.png ├── ecr ├── README.md ├── _cim.yml └── ecr.stack.yml ├── ecs ├── _cim.yml └── ecs.stack.yml ├── rds ├── _cim.yml ├── rds.policy.json └── rds.stack.yml ├── vpc ├── _cim.yml └── vpc.stack.yml └── wordpress ├── _cim.yml ├── src ├── Dockerfile └── docker-compose.yml └── wp.stack.yml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 thestacks-io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Headless Wordpress Deployment using AWS CloudFormation 2 | Quikly spin up a headless Wordpress deployment using AWS CloudFormation. 3 | 4 | [![](diagram1.png)](diagram1.png) 5 | 6 | 7 | [![](diagram2.png)](diagram2.png) 8 | 9 | Infrastructure as Code (IaC) is the recommended way to manage the cloud infrastructure that your 10 | application runs on. IaC allows you to incrementailly add/remove infrastructure as your application changes. 11 | 12 | IaC really shines when you need to spin up a new environment. Lets say you get a huge customer who wants 13 | to be on their own instance. You can be up in running withing the hour. 14 | 15 | This project contains 5 CloudFormation scripts. They must be created in order because they depend on each other: 16 | 1. VPC 17 | 2. ECS 18 | 3. RDS 19 | 4. ECR 20 | 5. Wordpress 21 | 22 | # Prerequisites 23 | - [AWS Account](https://aws.amazon.com/) 24 | - [EC2 Key Pair](https://console.aws.amazon.com/ec2/v2/home) 25 | - cim - (`npm install -g cim`) 26 | - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/installing.html) 27 | 28 | 29 | # Stacks 30 | 31 | ## VPC 32 | This creates the [Amazon Virtual Private Cloud](https://aws.amazon.com/vpc/) that our ECS cluster and RDS database will run in. 33 | > Amazon Virtual Private Cloud (Amazon VPC) lets you provision a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. 34 | ``` 35 | cd vpc 36 | cim stack-up 37 | ``` 38 | 39 | ## ECS 40 | This creates an [Elastic Container Service](https://aws.amazon.com/ecs/) that our EC2's will run in. 41 | > Amazon Elastic Container Service (Amazon ECS) is a highly scalable, high-performance container orchestration service that supports Docker containers and allows you to easily run and scale containerized applications on AWS. 42 | ``` 43 | cd vpc 44 | cim stack-up 45 | ``` 46 | 47 | ## RDS 48 | This creates a [Relational Database Service](https://aws.amazon.com/rds/) database cluster that our Wordpress application will use. 49 | > Amazon Relational Database Service (Amazon RDS) makes it easy to set up, operate, and scale a relational database in the cloud. 50 | ``` 51 | cd rds 52 | export DatabaseUsername="???"; export DatabasePassword="???"; cim stack-up 53 | ``` 54 | 55 | ## ECR 56 | This creates an [Elastic Container Registry](https://aws.amazon.com/ecr/) that will hold the docker images of our wordpress service. 57 | > Amazon Elastic Container Registry (ECR) is a fully-managed Docker container registry that makes it easy for developers to store, manage, and deploy Docker container images. 58 | ``` 59 | cd ecr 60 | cim stack-up 61 | ``` 62 | 63 | ### Wordpress 64 | Before we can launch this cloudformation stack. We need to push our service image to ECR. 65 | #### Push Image 66 | ``` 67 | cd wordpress/src 68 | ``` 69 | - [Registry Authentication](http://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) 70 | - `aws ecr get-login --registry-ids ` 71 | - copy/past output to perform docker login, also append `/headless-wp` to the repository url. 72 | - Build Image 73 | - `docker build -t headless-wp: .` 74 | - [Push Image](http://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-push-ecr-image.html) 75 | - `docker tag headless-wp: .dkr.ecr..amazonaws.com/headless-wp:latest` 76 | - `docker tag headless-wp: .dkr.ecr..amazonaws.com/headless-wp:` 77 | - `docker push .dkr.ecr..amazonaws.com/headless-wp` 78 | 79 | #### Update Version 80 | Make sure the `Version` parameter, in _cim.yml, matches the `version` tag from above. The ECS Task Definition will pull the image from ECR. 81 | 82 | #### Stack up 83 | Once the `Version` is set you can use `cim stack-up` to update the stack with the new version. 84 | 85 | ``` 86 | cd wordpress 87 | cim stack-up 88 | ``` 89 | 90 | # Wordpress 91 | Congratulations, your new Wordpress site is now available. 92 | 93 | First run through the Wordpress setup wizard. 94 | 95 | Next enable some of the plugins we added. 96 | 97 | Add a few blog posts and pages. 98 | 99 | Then check out the API. Ex: `https:///wp-json/wp/v2/posts` 100 | 101 | # Tear down 102 | ``` 103 | cd wordpress 104 | cim stack-delete 105 | 106 | cd ecr 107 | cim stack-delete 108 | 109 | cd rds 110 | cim stack-delete 111 | 112 | cd ecs 113 | cim stack-delete 114 | 115 | cd vpc 116 | cim stack-delete 117 | ``` 118 | -------------------------------------------------------------------------------- /diagram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgfindl/headless-wordpress/aee28ea713e8ec84d11db29fc32b4fc61f598a95/diagram1.png -------------------------------------------------------------------------------- /diagram2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgfindl/headless-wordpress/aee28ea713e8ec84d11db29fc32b4fc61f598a95/diagram2.png -------------------------------------------------------------------------------- /ecr/README.md: -------------------------------------------------------------------------------- 1 | # ECR 2 | Creates the ECR repository for each service. 3 | 4 | For each `service` you will need to create a `AWS::ECR::Repository` within the cloudformation.yml. 5 | 6 | The `Service1RepositoryUrl` output parameter is used when uploading docker images. An image is required prior to deploying the `ecs` stack. 7 | 8 | You also need to update the `policy` for each Repository. Give permission to only those users who need it. -------------------------------------------------------------------------------- /ecr/_cim.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | stack: 3 | name: headless-wp-ecr # Note: Update this with your stack name 4 | template: 5 | file: ecr.stack.yml 6 | bucket: cim-stack-artifacts # Note: Update this with your bucket name. Stacks are uploaded here prior to deployment.' 7 | 8 | # 9 | # Define stack input parameters. 10 | # 11 | parameters: 12 | Service1Name: 'headless-wp' 13 | 14 | # 15 | # Define stack capabilities required. 16 | # 17 | capabilities: 18 | - 'CAPABILITY_IAM' 19 | 20 | # 21 | # Define global tags. 22 | # 23 | tags: 24 | app: 'headless-wp' 25 | -------------------------------------------------------------------------------- /ecr/ecr.stack.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Parameters: 3 | Service1Name: 4 | Type: String 5 | Description: ECR Repository Name 6 | 7 | 8 | Resources: 9 | 10 | Repository: 11 | Type: AWS::ECR::Repository 12 | Properties: 13 | RepositoryName: !Ref Service1Name 14 | RepositoryPolicyText: 15 | Version: "2012-10-17" 16 | Statement: 17 | - 18 | Sid: AllowPushPull 19 | Effect: Allow 20 | Principal: 21 | AWS: 22 | - !Sub "arn:aws:iam::${AWS::AccountId}:user/randy.findley" 23 | Action: 24 | - "ecr:GetDownloadUrlForLayer" 25 | - "ecr:BatchGetImage" 26 | - "ecr:BatchCheckLayerAvailability" 27 | - "ecr:PutImage" 28 | - "ecr:InitiateLayerUpload" 29 | - "ecr:UploadLayerPart" 30 | - "ecr:CompleteLayerUpload" 31 | 32 | Outputs: 33 | Service1Repository: 34 | Value: !Ref 'Repository' 35 | Export: 36 | Name: !Sub '${AWS::StackName}-Service1Repository' 37 | Service1RepositoryUrl: 38 | Value: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}" 39 | Export: 40 | Name: !Sub '${AWS::StackName}-Service1RepositoryUrl' 41 | -------------------------------------------------------------------------------- /ecs/_cim.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | stack: 3 | name: headless-wp-ecs # Note: Update this with your stack name 4 | template: 5 | file: ecs.stack.yml 6 | bucket: cim-stack-artifacts # Note: Update this with your bucket name. Stacks are uploaded here prior to deployment.' 7 | 8 | # 9 | # Define stack input parameters. 10 | # 11 | parameters: 12 | KeyPairName: 'bluefineng' 13 | VPCStack: 'headless-wp-vpc' 14 | TLD: 'bluefineng.com' 15 | Domain: 'cms.bluefineng.com' 16 | SSL: 'arn:aws:acm:us-east-1:132093761664:certificate/7a872cf1-4845-46f5-9a05-6183bafad0c2' 17 | 18 | # 19 | # Define stack capabilities required. 20 | # 21 | capabilities: 22 | - 'CAPABILITY_IAM' 23 | 24 | # 25 | # Define global tags. 26 | # 27 | tags: 28 | app: 'headless-wp' 29 | -------------------------------------------------------------------------------- /ecs/ecs.stack.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: EC2 configuration and ECS Cluster 3 | Parameters: 4 | KeyPairName: 5 | Type: AWS::EC2::KeyPair::KeyName 6 | Description: Name of an existing EC2 KeyPair to enable SSH access to the ECS instances. 7 | 8 | VPCStack: 9 | Type: String 10 | Description: VPC Stack Name 11 | 12 | DesiredCapacity: 13 | Type: Number 14 | Default: '1' 15 | Description: Number of instances to launch in your ECS cluster. 16 | 17 | MinSize: 18 | Type: Number 19 | Default: '1' 20 | Description: Minimum number of instances that can be launched in your ECS cluster. 21 | 22 | MaxSize: 23 | Type: Number 24 | Default: '2' 25 | Description: Maximum number of instances that can be launched in your ECS cluster. 26 | 27 | TargetCPUReservation: 28 | Description: Target CPU reservation % for autoscaling 29 | Type: Number 30 | Default: '75' 31 | 32 | TargetMemoryReservation: 33 | Description: Target Memory reservation % for autoscaling 34 | Type: Number 35 | Default: '75' 36 | 37 | InstanceType: 38 | Description: EC2 instance type 39 | Type: String 40 | Default: t2.micro 41 | AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large, 42 | m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge, 43 | c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge, 44 | c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge, 45 | r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge] 46 | ConstraintDescription: Please choose a valid instance type. 47 | 48 | TLD: 49 | Type: String 50 | Description: TLD name needed by Route53 to perform DNS (example.com) 51 | Default: '' 52 | 53 | Domain: 54 | Type: String 55 | Description: Domain name for your cms (cms.example.com) 56 | Default: '' 57 | 58 | SSL: 59 | Type: String 60 | Description: SSL Arn for your Domain 61 | Default: '' 62 | 63 | Conditions: 64 | UseCustomDomain: !And 65 | - !Not [!Equals [!Ref TLD, '']] 66 | - !Not [!Equals [!Ref Domain, '']] 67 | - !Not [!Equals [!Ref SSL, '']] 68 | 69 | Mappings: 70 | 71 | # These are the latest ECS optimized AMIs as of August 2017: 72 | # 73 | # amzn-ami-2017.03.f-amazon-ecs-optimized 74 | # ECS agent: 1.14.4 75 | # Docker: 17.03.2-ce 76 | # ecs-init: 1.14.4-1 77 | # 78 | # You can find the latest available on this page of our documentation: 79 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html 80 | # (note the AMI identifier is region specific) 81 | 82 | AWSRegionToAMI: 83 | us-east-2: 84 | AMI: ami-64300001 85 | us-east-1: 86 | AMI: ami-aff65ad2 87 | us-west-2: 88 | AMI: ami-40ddb938 89 | us-west-1: 90 | AMI: ami-69677709 91 | eu-west-3: 92 | AMI: ami-250eb858 93 | eu-west-2: 94 | AMI: ami-2218f945 95 | eu-west-1: 96 | AMI: ami-2d386654 97 | eu-central-1: 98 | AMI: ami-9fc39c74 99 | ap-northeast-2: 100 | AMI: ami-9d56f9f3 101 | ap-northeast-1: 102 | AMI: ami-a99d8ad5 103 | ap-southeast-2: 104 | AMI: ami-efda148d 105 | ap-southeast-1: 106 | AMI: ami-846144f8 107 | ca-central-1: 108 | AMI: ami-897ff9ed 109 | ap-south-1: 110 | AMI: ami-72edc81d 111 | sa-east-1: 112 | AMI: ami-4a7e2826 113 | 114 | Resources: 115 | 116 | # 117 | # Security Groups 118 | # 119 | 120 | # This security group defines who/where is allowed to access the Application Load Balancer. 121 | # By default, we've opened this up to the public internet (0.0.0.0/0) but can you restrict 122 | # it further if you want. 123 | LoadBalancerSecurityGroup: 124 | Type: AWS::EC2::SecurityGroup 125 | Properties: 126 | VpcId: 127 | Fn::ImportValue: 128 | !Sub "${VPCStack}-VPCID" 129 | GroupDescription: Access to the load balancer that sits in front of ECS 130 | SecurityGroupIngress: 131 | # Allow access from anywhere to our ECS services 132 | - CidrIp: 0.0.0.0/0 133 | IpProtocol: -1 134 | 135 | # This security group defines who/where is allowed to access the ECS hosts directly. 136 | # By default we're just allowing access from the load balancer. If you want to SSH 137 | # into the hosts, or expose non-load balanced services you can open their ports here. 138 | ECSHostSecurityGroup: 139 | Type: AWS::EC2::SecurityGroup 140 | Properties: 141 | VpcId: 142 | Fn::ImportValue: 143 | !Sub "${VPCStack}-VPCID" 144 | GroupDescription: Access to the ECS hosts and the tasks/containers that run on them 145 | SecurityGroupIngress: 146 | # Only allow inbound access to ECS from the ELB 147 | - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 148 | IpProtocol: -1 149 | 150 | # 151 | # Load Balancers 152 | # 153 | LoadBalancer: 154 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 155 | Properties: 156 | Name: !Sub ${AWS::StackName}-lb 157 | Subnets: 158 | - Fn::ImportValue: 159 | !Sub "${VPCStack}-PublicSubnet1ID" 160 | - Fn::ImportValue: 161 | !Sub "${VPCStack}-PublicSubnet2ID" 162 | SecurityGroups: 163 | - !Ref LoadBalancerSecurityGroup 164 | 165 | # We define a default target group here, as this is a mandatory Parameters 166 | # when creating an Application Load Balancer Listener. This is not used, instead 167 | # a target group is created per-service in each service template 168 | DefaultTargetGroup: 169 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 170 | Properties: 171 | Name: !Sub ${AWS::StackName}-default 172 | VpcId: 173 | Fn::ImportValue: 174 | !Sub "${VPCStack}-VPCID" 175 | Port: 80 176 | Protocol: HTTP 177 | 178 | LoadBalancerListenerHttp: 179 | Type: AWS::ElasticLoadBalancingV2::Listener 180 | Properties: 181 | LoadBalancerArn: !Ref LoadBalancer 182 | Port: 80 183 | Protocol: 'HTTP' 184 | DefaultActions: 185 | - Type: forward 186 | TargetGroupArn: !Ref DefaultTargetGroup 187 | 188 | LoadBalancerListenerHttps: 189 | Type: AWS::ElasticLoadBalancingV2::Listener 190 | Condition: UseCustomDomain 191 | Properties: 192 | LoadBalancerArn: !Ref LoadBalancer 193 | Port: 443 194 | Protocol: 'HTTPS' 195 | Certificates: 196 | - CertificateArn: !Ref SSL 197 | DefaultActions: 198 | - Type: forward 199 | TargetGroupArn: !Ref DefaultTargetGroup 200 | 201 | # 202 | # Route53 DNS record set to map our domain to API Gateway 203 | # 204 | DomainDNS: 205 | Type: AWS::Route53::RecordSetGroup 206 | Condition: UseCustomDomain 207 | Properties: 208 | HostedZoneName: 209 | Fn::Join: 210 | - '' 211 | - - !Ref TLD 212 | - '.' 213 | RecordSets: 214 | - 215 | Name: !Ref Domain 216 | Type: 'A' 217 | AliasTarget: 218 | HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID 219 | DNSName: !GetAtt LoadBalancer.DNSName 220 | 221 | # 222 | # ECS 223 | # 224 | 225 | # ECS/EC2 Roles 226 | # 227 | # This IAM Role is attached to all of the ECS hosts. It is based on the default role 228 | # published here: 229 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html 230 | # 231 | # You can add other IAM policy statements here to allow access from your ECS hosts 232 | # to other AWS services. Please note that this role will be used by ALL containers 233 | # running on the ECS host. 234 | 235 | ECSRole: 236 | Type: AWS::IAM::Role 237 | Properties: 238 | AssumeRolePolicyDocument: 239 | Statement: 240 | - Effect: Allow 241 | Principal: 242 | Service: [ec2.amazonaws.com] 243 | Action: ['sts:AssumeRole'] 244 | Path: / 245 | Policies: 246 | - PolicyName: ecs-service 247 | PolicyDocument: 248 | Statement: 249 | - Effect: Allow 250 | Action: 251 | - 'ecs:CreateCluster' 252 | - 'ecs:DeregisterContainerInstance' 253 | - 'ecs:DiscoverPollEndpoint' 254 | - 'ecs:Poll' 255 | - 'ecs:RegisterContainerInstance' 256 | - 'ecs:StartTelemetrySession' 257 | - 'ecs:Submit*' 258 | - 'logs:CreateLogStream' 259 | - 'logs:PutLogEvents' 260 | - 'ecr:BatchCheckLayerAvailability' 261 | - 'ecr:BatchGetImage' 262 | - 'ecr:GetDownloadUrlForLayer' 263 | - 'ecr:GetAuthorizationToken' 264 | Resource: '*' 265 | 266 | ECSInstanceProfile: 267 | Type: AWS::IAM::InstanceProfile 268 | Properties: 269 | Path: / 270 | Roles: 271 | - !Ref ECSRole 272 | 273 | # ECS Cluster 274 | ECSCluster: 275 | Type: AWS::ECS::Cluster 276 | Properties: 277 | ClusterName: !Ref AWS::StackName 278 | 279 | ECSLaunchConfiguration: 280 | Type: AWS::AutoScaling::LaunchConfiguration 281 | Properties: 282 | ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMI] 283 | SecurityGroups: 284 | - !Ref ECSHostSecurityGroup 285 | InstanceType: !Ref InstanceType 286 | IamInstanceProfile: !Ref ECSInstanceProfile 287 | KeyName: !Ref 'KeyPairName' 288 | UserData: 289 | Fn::Base64: !Sub | 290 | #!/bin/bash -xe 291 | echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config 292 | yum install -y aws-cfn-bootstrap 293 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region} 294 | 295 | ECSAutoScalingGroup: 296 | Type: AWS::AutoScaling::AutoScalingGroup 297 | Properties: 298 | VPCZoneIdentifier: 299 | - Fn::ImportValue: 300 | !Sub "${VPCStack}-PublicSubnet1ID" 301 | - Fn::ImportValue: 302 | !Sub "${VPCStack}-PublicSubnet2ID" 303 | LaunchConfigurationName: !Ref ECSLaunchConfiguration 304 | MinSize: !Ref MinSize 305 | MaxSize: !Ref MaxSize 306 | DesiredCapacity: !Ref DesiredCapacity 307 | CreationPolicy: 308 | ResourceSignal: 309 | Timeout: PT15M 310 | UpdatePolicy: 311 | AutoScalingReplacingUpdate: 312 | WillReplace: 'true' 313 | AutoScalingRollingUpdate: 314 | MinInstancesInService: '1' 315 | MaxBatchSize: '1' 316 | PauseTime: PT15M 317 | WaitOnResourceSignals: 'true' 318 | 319 | CPUReservationPolicy: 320 | Type: AWS::AutoScaling::ScalingPolicy 321 | Properties: 322 | AdjustmentType: ChangeInCapacity 323 | AutoScalingGroupName: !Ref ECSAutoScalingGroup 324 | Cooldown: 300 325 | PolicyType: TargetTrackingScaling 326 | TargetTrackingConfiguration: 327 | CustomizedMetricSpecification: 328 | Dimensions: 329 | - Name: ClusterName 330 | Value: !Ref ECSCluster 331 | MetricName: CPUReservation 332 | Namespace: AWS/ECS 333 | Statistic: Average 334 | TargetValue: !Ref TargetCPUReservation 335 | 336 | MemoryReservationPolicy: 337 | Type: AWS::AutoScaling::ScalingPolicy 338 | Properties: 339 | AdjustmentType: ChangeInCapacity 340 | AutoScalingGroupName: !Ref ECSAutoScalingGroup 341 | Cooldown: 300 342 | PolicyType: TargetTrackingScaling 343 | TargetTrackingConfiguration: 344 | CustomizedMetricSpecification: 345 | Dimensions: 346 | - Name: ClusterName 347 | Value: !Ref ECSCluster 348 | MetricName: MemoryReservation 349 | Namespace: AWS/ECS 350 | Statistic: Average 351 | TargetValue: !Ref TargetMemoryReservation 352 | 353 | Outputs: 354 | ECSCluster: 355 | Value: !Ref 'ECSCluster' 356 | Export: 357 | Name: !Sub '${AWS::StackName}-ECSCluster' 358 | ECSHostSecurityGroup: 359 | Value: !Ref 'ECSHostSecurityGroup' 360 | Export: 361 | Name: !Sub '${AWS::StackName}-ECSHostSecurityGroup' 362 | LoadBalancerListenerHttp: 363 | Value: !Ref 'LoadBalancerListenerHttp' 364 | Export: 365 | Name: !Sub '${AWS::StackName}-LoadBalancerListenerHttp' 366 | LoadBalancerListenerHttps: 367 | Condition: UseCustomDomain 368 | Value: !Ref 'LoadBalancerListenerHttps' 369 | Export: 370 | Name: !Sub '${AWS::StackName}-LoadBalancerListenerHttps' 371 | LoadBalancerUrl: 372 | Value: !GetAtt LoadBalancer.DNSName 373 | Export: 374 | Name: !Sub '${AWS::StackName}-LoadBalancerUrl' 375 | CustomDomainUrl: 376 | Description: URL of your API endpoint 377 | Condition: UseCustomDomain 378 | Value: !Join 379 | - '' 380 | - - 'https://' 381 | - !Ref Domain 382 | - '/' 383 | Export: 384 | Name: !Sub '${AWS::StackName}-CustomDomainUrl' 385 | DefaultTargetGroup: 386 | Value: !Ref DefaultTargetGroup 387 | Export: 388 | Name: !Sub '${AWS::StackName}-DefaultTargetGroup' 389 | -------------------------------------------------------------------------------- /rds/_cim.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | stack: 3 | name: headless-wp-rds # Note: Update this with your stack name 4 | template: 5 | file: rds.stack.yml 6 | bucket: cim-stack-artifacts # Note: Update this with your bucket name. Stacks are uploaded here prior to deployment.' 7 | policy: 8 | file: rds.policy.json 9 | bucket: cim-stack-artifacts 10 | # 11 | # Define stack input parameters. 12 | # 13 | parameters: 14 | VPCStack: 'headless-wp-vpc' 15 | ECSStack: 'headless-wp-ecs' 16 | DatabaseUsername: '${env.DatabaseUsername}' 17 | DatabasePassword: '${env.DatabasePassword}' 18 | 19 | # 20 | # Define stack capabilities required. 21 | # 22 | capabilities: 23 | - 'CAPABILITY_IAM' 24 | 25 | # 26 | # Define global tags. 27 | # 28 | tags: 29 | app: 'headless-wp' 30 | -------------------------------------------------------------------------------- /rds/rds.policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Statement" : [ 3 | { 4 | "Effect" : "Deny", 5 | "Action" : [ 6 | "Update:Replace", 7 | "Update:Delete" 8 | ], 9 | "Principal": "*", 10 | "Resource" : "*", 11 | "Condition" : { 12 | "StringEquals" : { 13 | "ResourceType" : [ 14 | "AWS::RDS::DBInstance", 15 | "AWS::RDS::DBCluster" 16 | ] 17 | } 18 | } 19 | }, 20 | { 21 | "Effect" : "Allow", 22 | "Action" : "Update:*", 23 | "Principal": "*", 24 | "Resource" : "*" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /rds/rds.stack.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: EC2 configuration and ECS Cluster 3 | Parameters: 4 | 5 | VPCStack: 6 | Type: String 7 | Description: VPC Stack Name 8 | 9 | ECSStack: 10 | Type: String 11 | Description: ECS Stack Name 12 | 13 | DatabaseUsername: 14 | AllowedPattern: "^[a-zA-Z0-9]*$" 15 | ConstraintDescription: must be between 1 to 16 alphanumeric characters. 16 | Description: The database admin account user name, between 1 to 16 alphanumeric characters. 17 | MaxLength: '16' 18 | MinLength: '1' 19 | Type: String 20 | 21 | DatabasePassword: 22 | AllowedPattern: "^[a-zA-Z0-9]*$" 23 | ConstraintDescription: must be between 8 to 41 alphanumeric characters. 24 | Description: The database admin account password, between 8 to 41 alphanumeric characters. 25 | MaxLength: '41' 26 | MinLength: '8' 27 | NoEcho: 'true' 28 | Type: String 29 | 30 | InstanceType: 31 | Type: String 32 | Default: 'db.t2.small' 33 | 34 | 35 | Resources: 36 | 37 | DBSubnetGroup: 38 | Type: AWS::RDS::DBSubnetGroup 39 | Properties: 40 | DBSubnetGroupDescription: CloudFormation managed DB subnet group. 41 | SubnetIds: 42 | - Fn::ImportValue: 43 | !Sub "${VPCStack}-PublicSubnet1ID" 44 | - Fn::ImportValue: 45 | !Sub "${VPCStack}-PublicSubnet2ID" 46 | 47 | ClusterSecurityGroup: 48 | Type: 'AWS::EC2::SecurityGroup' 49 | Properties: 50 | GroupDescription: Access to the RDS database 51 | SecurityGroupIngress: 52 | - IpProtocol: tcp 53 | FromPort: 3306 54 | ToPort: 3306 55 | SourceSecurityGroupId: 56 | Fn::ImportValue: 57 | !Sub "${ECSStack}-ECSHostSecurityGroup" 58 | VpcId: 59 | Fn::ImportValue: 60 | !Sub "${VPCStack}-VPCID" 61 | 62 | RDSCluster: 63 | Type: AWS::RDS::DBCluster 64 | Properties: 65 | MasterUsername: !Ref DatabaseUsername 66 | MasterUserPassword: !Ref DatabasePassword 67 | Engine: aurora 68 | #EngineMode: serverless 69 | DBSubnetGroupName: !Ref DBSubnetGroup 70 | VpcSecurityGroupIds: 71 | - !Ref ClusterSecurityGroup 72 | DBClusterParameterGroupName: !Ref RDSDBClusterParameterGroup 73 | BackupRetentionPeriod: 7 74 | PreferredBackupWindow: 01:00-02:00 75 | PreferredMaintenanceWindow: mon:03:00-mon:04:00 76 | 77 | PrimaryInstance: 78 | Type: AWS::RDS::DBInstance 79 | Properties: 80 | DBSubnetGroupName: !Ref DBSubnetGroup 81 | DBParameterGroupName: !Ref RDSDBParameterGroup 82 | Engine: aurora 83 | DBClusterIdentifier: !Ref RDSCluster 84 | PubliclyAccessible: 'true' 85 | AvailabilityZone: 86 | Fn::ImportValue: 87 | !Sub "${VPCStack}-AvailabilityZone1" 88 | DBInstanceClass: !Ref InstanceType 89 | 90 | SecondaryInstance: 91 | Type: AWS::RDS::DBInstance 92 | Properties: 93 | DBSubnetGroupName: !Ref DBSubnetGroup 94 | DBParameterGroupName: !Ref RDSDBParameterGroup 95 | Engine: aurora 96 | DBClusterIdentifier: !Ref RDSCluster 97 | PubliclyAccessible: 'true' 98 | AvailabilityZone: 99 | Fn::ImportValue: 100 | !Sub "${VPCStack}-AvailabilityZone2" 101 | DBInstanceClass: !Ref InstanceType 102 | 103 | 104 | RDSDBClusterParameterGroup: 105 | Type: AWS::RDS::DBClusterParameterGroup 106 | Properties: 107 | Description: CloudFormation Sample Aurora Cluster Parameter Group 108 | Family: aurora5.6 109 | Parameters: 110 | time_zone: US/Eastern 111 | 112 | RDSDBParameterGroup: 113 | Type: AWS::RDS::DBParameterGroup 114 | Properties: 115 | Description: CloudFormation Sample Aurora Parameter Group 116 | Family: aurora5.6 117 | Parameters: 118 | sql_mode: IGNORE_SPACE 119 | 120 | Outputs: 121 | DatabaseUsername: 122 | Value: !Ref 'DatabaseUsername' 123 | Export: 124 | Name: !Sub '${AWS::StackName}-DatabaseUsername' 125 | DatabasePassword: 126 | Value: !Ref 'DatabasePassword' 127 | Export: 128 | Name: !Sub '${AWS::StackName}-DatabasePassword' 129 | RDSClusterEndpoint: 130 | Value: !GetAtt RDSCluster.Endpoint.Address 131 | Export: 132 | Name: !Sub '${AWS::StackName}-RDSClusterEndpoint' -------------------------------------------------------------------------------- /vpc/_cim.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | stack: 3 | name: headless-wp-vpc # Note: Update this with your stack name 4 | template: 5 | file: vpc.stack.yml 6 | bucket: cim-stack-artifacts # Note: Update this with your bucket name. Stacks are uploaded here prior to deployment.' 7 | 8 | # 9 | # Define stack input parameters. 10 | # 11 | parameters: 12 | KeyPairName: 'bluefineng' 13 | 14 | # 15 | # Define stack capabilities required. 16 | # 17 | capabilities: 18 | - 'CAPABILITY_IAM' 19 | 20 | # 21 | # Define global tags. 22 | # 23 | tags: 24 | app: 'headless-wp' 25 | -------------------------------------------------------------------------------- /vpc/vpc.stack.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: VPC 4 | Parameters: 5 | AvailabilityZones: 6 | Description: 'List of Availability Zones to use for the subnets in the VPC. Note: 7 | The logical order is preserved.' 8 | Type: List 9 | Default: 'us-east-1a, us-east-1b' 10 | CreateAdditionalPrivateSubnets: 11 | AllowedValues: 12 | - 'true' 13 | - 'false' 14 | Default: 'false' 15 | Description: Set to true to create a network ACL protected subnet in each Availability 16 | Zone. If false, the CIDR parameters for those subnets will be ignored. If true, 17 | it also requires that the 'Create private subnets' parameter is also true to 18 | have any effect. 19 | Type: String 20 | CreatePrivateSubnets: 21 | AllowedValues: 22 | - 'true' 23 | - 'false' 24 | Default: 'false' 25 | Description: Set to false to create only public subnets. If false, the CIDR parameters 26 | for ALL private subnets will be ignored. 27 | Type: String 28 | KeyPairName: 29 | Description: Public/private key pairs allow you to securely connect to your NAT 30 | instance after it launches. This is used only if the region does not support 31 | NAT gateways. 32 | Type: AWS::EC2::KeyPair::KeyName 33 | NATInstanceType: 34 | AllowedValues: 35 | - t2.nano 36 | - t2.micro 37 | - t2.small 38 | - t2.medium 39 | - t2.large 40 | - m3.medium 41 | - m3.large 42 | - m4.large 43 | Default: t2.small 44 | Description: Amazon EC2 instance type for the NAT instances. This is used only 45 | if the region does not support NAT gateways. 46 | Type: String 47 | NumberOfAZs: 48 | AllowedValues: 49 | - '2' 50 | - '3' 51 | - '4' 52 | Default: '2' 53 | Description: Number of Availability Zones to use in the VPC. This must match your 54 | selections in the list of Availability Zones parameter. 55 | Type: String 56 | PrivateSubnet1ACIDR: 57 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 58 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 59 | Default: 10.0.0.0/19 60 | Description: CIDR block for private subnet 1A located in Availability Zone 1 61 | Type: String 62 | PrivateSubnet1BCIDR: 63 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 64 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 65 | Default: 10.0.192.0/21 66 | Description: CIDR block for private subnet 1B with dedicated network ACL located 67 | in Availability Zone 1 68 | Type: String 69 | PrivateSubnet2ACIDR: 70 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 71 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 72 | Default: 10.0.32.0/19 73 | Description: CIDR block for private subnet 2A located in Availability Zone 2 74 | Type: String 75 | PrivateSubnet2BCIDR: 76 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 77 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 78 | Default: 10.0.200.0/21 79 | Description: CIDR block for private subnet 2B with dedicated network ACL located 80 | in Availability Zone 2 81 | Type: String 82 | PrivateSubnet3ACIDR: 83 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 84 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 85 | Default: 10.0.64.0/19 86 | Description: CIDR block for private subnet 3A located in Availability Zone 3 87 | Type: String 88 | PrivateSubnet3BCIDR: 89 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 90 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 91 | Default: 10.0.208.0/21 92 | Description: CIDR block for private subnet 3B with dedicated network ACL located 93 | in Availability Zone 3 94 | Type: String 95 | PrivateSubnet4ACIDR: 96 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 97 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 98 | Default: 10.0.96.0/19 99 | Description: CIDR block for private subnet 4A located in Availability Zone 4 100 | Type: String 101 | PrivateSubnet4BCIDR: 102 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 103 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 104 | Default: 10.0.216.0/21 105 | Description: CIDR block for private subnet 4B with dedicated network ACL located 106 | in Availability Zone 4 107 | Type: String 108 | PublicSubnet1CIDR: 109 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 110 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 111 | Default: 10.0.128.0/20 112 | Description: CIDR block for the public DMZ subnet 1 located in Availability Zone 113 | 1 114 | Type: String 115 | PublicSubnet2CIDR: 116 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 117 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 118 | Default: 10.0.144.0/20 119 | Description: CIDR block for the public DMZ subnet 2 located in Availability Zone 120 | 2 121 | Type: String 122 | PublicSubnet3CIDR: 123 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 124 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 125 | Default: 10.0.160.0/20 126 | Description: CIDR block for the public DMZ subnet 3 located in Availability Zone 127 | 3 128 | Type: String 129 | PublicSubnet4CIDR: 130 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 131 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 132 | Default: 10.0.176.0/20 133 | Description: CIDR block for the public DMZ subnet 4 located in Availability Zone 134 | 4 135 | Type: String 136 | VPCCIDR: 137 | AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" 138 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 139 | Default: 10.0.0.0/16 140 | Description: CIDR block for the VPC 141 | Type: String 142 | VPCTenancy: 143 | AllowedValues: 144 | - default 145 | - dedicated 146 | Default: default 147 | Description: The allowed tenancy of instances launched into the VPC 148 | Type: String 149 | Mappings: 150 | AWSAMIRegionMap: 151 | AMI: 152 | AWSNATHVM: amzn-ami-vpc-nat-hvm-2017.03.0.20170401-x86_64-ebs 153 | us-gov-west-1: 154 | AWSNATHVM: ami-3f0a8f5e 155 | Conditions: 156 | 3AZCondition: 157 | Fn::Or: 158 | - Fn::Equals: 159 | - Ref: NumberOfAZs 160 | - '3' 161 | - Condition: 4AZCondition 162 | 4AZCondition: 163 | Fn::Equals: 164 | - Ref: NumberOfAZs 165 | - '4' 166 | AdditionalPrivateSubnetsCondition: 167 | Fn::And: 168 | - Fn::Equals: 169 | - Ref: CreatePrivateSubnets 170 | - 'true' 171 | - Fn::Equals: 172 | - Ref: CreateAdditionalPrivateSubnets 173 | - 'true' 174 | AdditionalPrivateSubnets&3AZCondition: 175 | Fn::And: 176 | - Condition: AdditionalPrivateSubnetsCondition 177 | - Condition: 3AZCondition 178 | AdditionalPrivateSubnets&4AZCondition: 179 | Fn::And: 180 | - Condition: AdditionalPrivateSubnetsCondition 181 | - Condition: 4AZCondition 182 | GovCloudCondition: 183 | Fn::Equals: 184 | - Ref: AWS::Region 185 | - us-gov-west-1 186 | NATInstanceCondition: 187 | Fn::And: 188 | - Condition: PrivateSubnetsCondition 189 | - Condition: GovCloudCondition 190 | NATGatewayCondition: 191 | Fn::And: 192 | - Condition: PrivateSubnetsCondition 193 | - Fn::Not: 194 | - Condition: GovCloudCondition 195 | NATInstance&3AZCondition: 196 | Fn::And: 197 | - Condition: NATInstanceCondition 198 | - Condition: 3AZCondition 199 | NATInstance&4AZCondition: 200 | Fn::And: 201 | - Condition: NATInstanceCondition 202 | - Condition: 4AZCondition 203 | NATGateway&3AZCondition: 204 | Fn::And: 205 | - Condition: NATGatewayCondition 206 | - Condition: 3AZCondition 207 | NATGateway&4AZCondition: 208 | Fn::And: 209 | - Condition: NATGatewayCondition 210 | - Condition: 4AZCondition 211 | NVirginiaRegionCondition: 212 | Fn::Equals: 213 | - Ref: AWS::Region 214 | - us-east-1 215 | PrivateSubnetsCondition: 216 | Fn::Equals: 217 | - Ref: CreatePrivateSubnets 218 | - 'true' 219 | PrivateSubnets&3AZCondition: 220 | Fn::And: 221 | - Condition: PrivateSubnetsCondition 222 | - Condition: 3AZCondition 223 | PrivateSubnets&4AZCondition: 224 | Fn::And: 225 | - Condition: PrivateSubnetsCondition 226 | - Condition: 4AZCondition 227 | S3VPCEndpointCondition: 228 | Fn::And: 229 | - Condition: PrivateSubnetsCondition 230 | - Fn::Not: 231 | - Fn::Or: 232 | - Fn::Equals: 233 | - Ref: AWS::Region 234 | - us-gov-west-1 235 | - Fn::Equals: 236 | - Ref: AWS::Region 237 | - cn-north-1 238 | Resources: 239 | DHCPOptions: 240 | Type: AWS::EC2::DHCPOptions 241 | Properties: 242 | DomainName: 243 | Fn::If: 244 | - NVirginiaRegionCondition 245 | - ec2.internal 246 | - Fn::Join: 247 | - '' 248 | - - Ref: AWS::Region 249 | - ".compute.internal" 250 | DomainNameServers: 251 | - AmazonProvidedDNS 252 | VPC: 253 | Type: AWS::EC2::VPC 254 | Properties: 255 | CidrBlock: 256 | Ref: VPCCIDR 257 | InstanceTenancy: 258 | Ref: VPCTenancy 259 | EnableDnsSupport: 'true' 260 | EnableDnsHostnames: 'true' 261 | Tags: 262 | - Key: Name 263 | Value: 264 | Ref: AWS::StackName 265 | VPCDHCPOptionsAssociation: 266 | Type: AWS::EC2::VPCDHCPOptionsAssociation 267 | Properties: 268 | VpcId: 269 | Ref: VPC 270 | DhcpOptionsId: 271 | Ref: DHCPOptions 272 | InternetGateway: 273 | Type: AWS::EC2::InternetGateway 274 | Properties: 275 | Tags: 276 | - Key: Name 277 | Value: 278 | Ref: AWS::StackName 279 | - Key: Network 280 | Value: Public 281 | VPCGatewayAttachment: 282 | Type: AWS::EC2::VPCGatewayAttachment 283 | Properties: 284 | VpcId: 285 | Ref: VPC 286 | InternetGatewayId: 287 | Ref: InternetGateway 288 | PrivateSubnet1A: 289 | Condition: PrivateSubnetsCondition 290 | Type: AWS::EC2::Subnet 291 | Properties: 292 | VpcId: 293 | Ref: VPC 294 | CidrBlock: 295 | Ref: PrivateSubnet1ACIDR 296 | AvailabilityZone: 297 | Fn::Select: 298 | - '0' 299 | - Ref: AvailabilityZones 300 | Tags: 301 | - Key: Name 302 | Value: Private subnet 1A 303 | - Key: Network 304 | Value: Private 305 | PrivateSubnet1B: 306 | Condition: AdditionalPrivateSubnetsCondition 307 | Type: AWS::EC2::Subnet 308 | Properties: 309 | VpcId: 310 | Ref: VPC 311 | CidrBlock: 312 | Ref: PrivateSubnet1BCIDR 313 | AvailabilityZone: 314 | Fn::Select: 315 | - '0' 316 | - Ref: AvailabilityZones 317 | Tags: 318 | - Key: Name 319 | Value: Private subnet 1B 320 | - Key: Network 321 | Value: Private 322 | PrivateSubnet2A: 323 | Condition: PrivateSubnetsCondition 324 | Type: AWS::EC2::Subnet 325 | Properties: 326 | VpcId: 327 | Ref: VPC 328 | CidrBlock: 329 | Ref: PrivateSubnet2ACIDR 330 | AvailabilityZone: 331 | Fn::Select: 332 | - '1' 333 | - Ref: AvailabilityZones 334 | Tags: 335 | - Key: Name 336 | Value: Private subnet 2A 337 | - Key: Network 338 | Value: Private 339 | PrivateSubnet2B: 340 | Condition: AdditionalPrivateSubnetsCondition 341 | Type: AWS::EC2::Subnet 342 | Properties: 343 | VpcId: 344 | Ref: VPC 345 | CidrBlock: 346 | Ref: PrivateSubnet2BCIDR 347 | AvailabilityZone: 348 | Fn::Select: 349 | - '1' 350 | - Ref: AvailabilityZones 351 | Tags: 352 | - Key: Name 353 | Value: Private subnet 2B 354 | - Key: Network 355 | Value: Private 356 | PrivateSubnet3A: 357 | Condition: PrivateSubnets&3AZCondition 358 | Type: AWS::EC2::Subnet 359 | Properties: 360 | VpcId: 361 | Ref: VPC 362 | CidrBlock: 363 | Ref: PrivateSubnet3ACIDR 364 | AvailabilityZone: 365 | Fn::Select: 366 | - '2' 367 | - Ref: AvailabilityZones 368 | Tags: 369 | - Key: Name 370 | Value: Private subnet 3A 371 | - Key: Network 372 | Value: Private 373 | PrivateSubnet3B: 374 | Condition: AdditionalPrivateSubnets&3AZCondition 375 | Type: AWS::EC2::Subnet 376 | Properties: 377 | VpcId: 378 | Ref: VPC 379 | CidrBlock: 380 | Ref: PrivateSubnet3BCIDR 381 | AvailabilityZone: 382 | Fn::Select: 383 | - '2' 384 | - Ref: AvailabilityZones 385 | Tags: 386 | - Key: Name 387 | Value: Private subnet 3B 388 | - Key: Network 389 | Value: Private 390 | PrivateSubnet4A: 391 | Condition: PrivateSubnets&4AZCondition 392 | Type: AWS::EC2::Subnet 393 | Properties: 394 | VpcId: 395 | Ref: VPC 396 | CidrBlock: 397 | Ref: PrivateSubnet4ACIDR 398 | AvailabilityZone: 399 | Fn::Select: 400 | - '3' 401 | - Ref: AvailabilityZones 402 | Tags: 403 | - Key: Name 404 | Value: Private subnet 4A 405 | - Key: Network 406 | Value: Private 407 | PrivateSubnet4B: 408 | Condition: AdditionalPrivateSubnets&4AZCondition 409 | Type: AWS::EC2::Subnet 410 | Properties: 411 | VpcId: 412 | Ref: VPC 413 | CidrBlock: 414 | Ref: PrivateSubnet4BCIDR 415 | AvailabilityZone: 416 | Fn::Select: 417 | - '3' 418 | - Ref: AvailabilityZones 419 | Tags: 420 | - Key: Name 421 | Value: Private subnet 4B 422 | - Key: Network 423 | Value: Private 424 | PublicSubnet1: 425 | Type: AWS::EC2::Subnet 426 | Properties: 427 | VpcId: 428 | Ref: VPC 429 | CidrBlock: 430 | Ref: PublicSubnet1CIDR 431 | AvailabilityZone: 432 | Fn::Select: 433 | - '0' 434 | - Ref: AvailabilityZones 435 | Tags: 436 | - Key: Name 437 | Value: Public subnet 1 438 | - Key: Network 439 | Value: Public 440 | MapPublicIpOnLaunch: true 441 | PublicSubnet2: 442 | Type: AWS::EC2::Subnet 443 | Properties: 444 | VpcId: 445 | Ref: VPC 446 | CidrBlock: 447 | Ref: PublicSubnet2CIDR 448 | AvailabilityZone: 449 | Fn::Select: 450 | - '1' 451 | - Ref: AvailabilityZones 452 | Tags: 453 | - Key: Name 454 | Value: Public subnet 2 455 | - Key: Network 456 | Value: Public 457 | MapPublicIpOnLaunch: true 458 | PublicSubnet3: 459 | Condition: 3AZCondition 460 | Type: AWS::EC2::Subnet 461 | Properties: 462 | VpcId: 463 | Ref: VPC 464 | CidrBlock: 465 | Ref: PublicSubnet3CIDR 466 | AvailabilityZone: 467 | Fn::Select: 468 | - '2' 469 | - Ref: AvailabilityZones 470 | Tags: 471 | - Key: Name 472 | Value: Public subnet 3 473 | - Key: Network 474 | Value: Public 475 | MapPublicIpOnLaunch: true 476 | PublicSubnet4: 477 | Condition: 4AZCondition 478 | Type: AWS::EC2::Subnet 479 | Properties: 480 | VpcId: 481 | Ref: VPC 482 | CidrBlock: 483 | Ref: PublicSubnet4CIDR 484 | AvailabilityZone: 485 | Fn::Select: 486 | - '3' 487 | - Ref: AvailabilityZones 488 | Tags: 489 | - Key: Name 490 | Value: Public subnet 4 491 | - Key: Network 492 | Value: Public 493 | MapPublicIpOnLaunch: true 494 | PrivateSubnet1ARouteTable: 495 | Condition: PrivateSubnetsCondition 496 | Type: AWS::EC2::RouteTable 497 | Properties: 498 | VpcId: 499 | Ref: VPC 500 | Tags: 501 | - Key: Name 502 | Value: Private subnet 1A 503 | - Key: Network 504 | Value: Private 505 | PrivateSubnet1ARoute: 506 | Condition: PrivateSubnetsCondition 507 | Type: AWS::EC2::Route 508 | Properties: 509 | RouteTableId: 510 | Ref: PrivateSubnet1ARouteTable 511 | DestinationCidrBlock: 0.0.0.0/0 512 | InstanceId: 513 | Fn::If: 514 | - NATInstanceCondition 515 | - Ref: NATInstance1 516 | - Ref: AWS::NoValue 517 | NatGatewayId: 518 | Fn::If: 519 | - NATGatewayCondition 520 | - Ref: NATGateway1 521 | - Ref: AWS::NoValue 522 | PrivateSubnet1ARouteTableAssociation: 523 | Condition: PrivateSubnetsCondition 524 | Type: AWS::EC2::SubnetRouteTableAssociation 525 | Properties: 526 | SubnetId: 527 | Ref: PrivateSubnet1A 528 | RouteTableId: 529 | Ref: PrivateSubnet1ARouteTable 530 | PrivateSubnet2ARouteTable: 531 | Condition: PrivateSubnetsCondition 532 | Type: AWS::EC2::RouteTable 533 | Properties: 534 | VpcId: 535 | Ref: VPC 536 | Tags: 537 | - Key: Name 538 | Value: Private subnet 2A 539 | - Key: Network 540 | Value: Private 541 | PrivateSubnet2ARoute: 542 | Condition: PrivateSubnetsCondition 543 | Type: AWS::EC2::Route 544 | Properties: 545 | RouteTableId: 546 | Ref: PrivateSubnet2ARouteTable 547 | DestinationCidrBlock: 0.0.0.0/0 548 | InstanceId: 549 | Fn::If: 550 | - NATInstanceCondition 551 | - Ref: NATInstance2 552 | - Ref: AWS::NoValue 553 | NatGatewayId: 554 | Fn::If: 555 | - NATGatewayCondition 556 | - Ref: NATGateway2 557 | - Ref: AWS::NoValue 558 | PrivateSubnet2ARouteTableAssociation: 559 | Condition: PrivateSubnetsCondition 560 | Type: AWS::EC2::SubnetRouteTableAssociation 561 | Properties: 562 | SubnetId: 563 | Ref: PrivateSubnet2A 564 | RouteTableId: 565 | Ref: PrivateSubnet2ARouteTable 566 | PrivateSubnet3ARouteTable: 567 | Condition: PrivateSubnets&3AZCondition 568 | Type: AWS::EC2::RouteTable 569 | Properties: 570 | VpcId: 571 | Ref: VPC 572 | Tags: 573 | - Key: Name 574 | Value: Private subnet 3A 575 | - Key: Network 576 | Value: Private 577 | PrivateSubnet3ARoute: 578 | Condition: PrivateSubnets&3AZCondition 579 | Type: AWS::EC2::Route 580 | Properties: 581 | RouteTableId: 582 | Ref: PrivateSubnet3ARouteTable 583 | DestinationCidrBlock: 0.0.0.0/0 584 | InstanceId: 585 | Fn::If: 586 | - NATInstanceCondition 587 | - Ref: NATInstance3 588 | - Ref: AWS::NoValue 589 | NatGatewayId: 590 | Fn::If: 591 | - NATGatewayCondition 592 | - Ref: NATGateway3 593 | - Ref: AWS::NoValue 594 | PrivateSubnet3ARouteTableAssociation: 595 | Condition: PrivateSubnets&3AZCondition 596 | Type: AWS::EC2::SubnetRouteTableAssociation 597 | Properties: 598 | SubnetId: 599 | Ref: PrivateSubnet3A 600 | RouteTableId: 601 | Ref: PrivateSubnet3ARouteTable 602 | PrivateSubnet4ARouteTable: 603 | Condition: PrivateSubnets&4AZCondition 604 | Type: AWS::EC2::RouteTable 605 | Properties: 606 | VpcId: 607 | Ref: VPC 608 | Tags: 609 | - Key: Name 610 | Value: Private subnet 4A 611 | - Key: Network 612 | Value: Private 613 | PrivateSubnet4ARoute: 614 | Condition: PrivateSubnets&4AZCondition 615 | Type: AWS::EC2::Route 616 | Properties: 617 | RouteTableId: 618 | Ref: PrivateSubnet4ARouteTable 619 | DestinationCidrBlock: 0.0.0.0/0 620 | InstanceId: 621 | Fn::If: 622 | - NATInstanceCondition 623 | - Ref: NATInstance4 624 | - Ref: AWS::NoValue 625 | NatGatewayId: 626 | Fn::If: 627 | - NATGatewayCondition 628 | - Ref: NATGateway4 629 | - Ref: AWS::NoValue 630 | PrivateSubnet4ARouteTableAssociation: 631 | Condition: PrivateSubnets&4AZCondition 632 | Type: AWS::EC2::SubnetRouteTableAssociation 633 | Properties: 634 | SubnetId: 635 | Ref: PrivateSubnet4A 636 | RouteTableId: 637 | Ref: PrivateSubnet4ARouteTable 638 | PrivateSubnet1BRouteTable: 639 | Condition: AdditionalPrivateSubnetsCondition 640 | Type: AWS::EC2::RouteTable 641 | Properties: 642 | VpcId: 643 | Ref: VPC 644 | Tags: 645 | - Key: Name 646 | Value: Private subnet 1B 647 | - Key: Network 648 | Value: Private 649 | PrivateSubnet1BRoute: 650 | Condition: AdditionalPrivateSubnetsCondition 651 | Type: AWS::EC2::Route 652 | Properties: 653 | RouteTableId: 654 | Ref: PrivateSubnet1BRouteTable 655 | DestinationCidrBlock: 0.0.0.0/0 656 | InstanceId: 657 | Fn::If: 658 | - NATInstanceCondition 659 | - Ref: NATInstance1 660 | - Ref: AWS::NoValue 661 | NatGatewayId: 662 | Fn::If: 663 | - NATGatewayCondition 664 | - Ref: NATGateway1 665 | - Ref: AWS::NoValue 666 | PrivateSubnet1BRouteTableAssociation: 667 | Condition: AdditionalPrivateSubnetsCondition 668 | Type: AWS::EC2::SubnetRouteTableAssociation 669 | Properties: 670 | SubnetId: 671 | Ref: PrivateSubnet1B 672 | RouteTableId: 673 | Ref: PrivateSubnet1BRouteTable 674 | PrivateSubnet1BNetworkAcl: 675 | Condition: AdditionalPrivateSubnetsCondition 676 | Type: AWS::EC2::NetworkAcl 677 | Properties: 678 | VpcId: 679 | Ref: VPC 680 | Tags: 681 | - Key: Name 682 | Value: NACL Protected subnet 1 683 | - Key: Network 684 | Value: NACL Protected 685 | PrivateSubnet1BNetworkAclEntryInbound: 686 | Condition: AdditionalPrivateSubnetsCondition 687 | Type: AWS::EC2::NetworkAclEntry 688 | Properties: 689 | CidrBlock: 0.0.0.0/0 690 | Egress: 'false' 691 | NetworkAclId: 692 | Ref: PrivateSubnet1BNetworkAcl 693 | Protocol: "-1" 694 | RuleAction: allow 695 | RuleNumber: '100' 696 | PrivateSubnet1BNetworkAclEntryOutbound: 697 | Condition: AdditionalPrivateSubnetsCondition 698 | Type: AWS::EC2::NetworkAclEntry 699 | Properties: 700 | CidrBlock: 0.0.0.0/0 701 | Egress: 'true' 702 | NetworkAclId: 703 | Ref: PrivateSubnet1BNetworkAcl 704 | Protocol: "-1" 705 | RuleAction: allow 706 | RuleNumber: '100' 707 | PrivateSubnet1BNetworkAclAssociation: 708 | Condition: AdditionalPrivateSubnetsCondition 709 | Type: AWS::EC2::SubnetNetworkAclAssociation 710 | Properties: 711 | SubnetId: 712 | Ref: PrivateSubnet1B 713 | NetworkAclId: 714 | Ref: PrivateSubnet1BNetworkAcl 715 | PrivateSubnet2BRouteTable: 716 | Condition: AdditionalPrivateSubnetsCondition 717 | Type: AWS::EC2::RouteTable 718 | Properties: 719 | VpcId: 720 | Ref: VPC 721 | Tags: 722 | - Key: Name 723 | Value: Private subnet 2B 724 | - Key: Network 725 | Value: Private 726 | PrivateSubnet2BRoute: 727 | Condition: AdditionalPrivateSubnetsCondition 728 | Type: AWS::EC2::Route 729 | Properties: 730 | RouteTableId: 731 | Ref: PrivateSubnet2BRouteTable 732 | DestinationCidrBlock: 0.0.0.0/0 733 | InstanceId: 734 | Fn::If: 735 | - NATInstanceCondition 736 | - Ref: NATInstance2 737 | - Ref: AWS::NoValue 738 | NatGatewayId: 739 | Fn::If: 740 | - NATGatewayCondition 741 | - Ref: NATGateway2 742 | - Ref: AWS::NoValue 743 | PrivateSubnet2BRouteTableAssociation: 744 | Condition: AdditionalPrivateSubnetsCondition 745 | Type: AWS::EC2::SubnetRouteTableAssociation 746 | Properties: 747 | SubnetId: 748 | Ref: PrivateSubnet2B 749 | RouteTableId: 750 | Ref: PrivateSubnet2BRouteTable 751 | PrivateSubnet2BNetworkAcl: 752 | Condition: AdditionalPrivateSubnetsCondition 753 | Type: AWS::EC2::NetworkAcl 754 | Properties: 755 | VpcId: 756 | Ref: VPC 757 | Tags: 758 | - Key: Name 759 | Value: NACL Protected subnet 2 760 | - Key: Network 761 | Value: NACL Protected 762 | PrivateSubnet2BNetworkAclEntryInbound: 763 | Condition: AdditionalPrivateSubnetsCondition 764 | Type: AWS::EC2::NetworkAclEntry 765 | Properties: 766 | CidrBlock: 0.0.0.0/0 767 | Egress: 'false' 768 | NetworkAclId: 769 | Ref: PrivateSubnet2BNetworkAcl 770 | Protocol: "-1" 771 | RuleAction: allow 772 | RuleNumber: '100' 773 | PrivateSubnet2BNetworkAclEntryOutbound: 774 | Condition: AdditionalPrivateSubnetsCondition 775 | Type: AWS::EC2::NetworkAclEntry 776 | Properties: 777 | CidrBlock: 0.0.0.0/0 778 | Egress: 'true' 779 | NetworkAclId: 780 | Ref: PrivateSubnet2BNetworkAcl 781 | Protocol: "-1" 782 | RuleAction: allow 783 | RuleNumber: '100' 784 | PrivateSubnet2BNetworkAclAssociation: 785 | Condition: AdditionalPrivateSubnetsCondition 786 | Type: AWS::EC2::SubnetNetworkAclAssociation 787 | Properties: 788 | SubnetId: 789 | Ref: PrivateSubnet2B 790 | NetworkAclId: 791 | Ref: PrivateSubnet2BNetworkAcl 792 | PrivateSubnet3BRouteTable: 793 | Condition: AdditionalPrivateSubnets&3AZCondition 794 | Type: AWS::EC2::RouteTable 795 | Properties: 796 | VpcId: 797 | Ref: VPC 798 | Tags: 799 | - Key: Name 800 | Value: Private subnet 3B 801 | - Key: Network 802 | Value: Private 803 | PrivateSubnet3BRoute: 804 | Condition: AdditionalPrivateSubnets&3AZCondition 805 | Type: AWS::EC2::Route 806 | Properties: 807 | RouteTableId: 808 | Ref: PrivateSubnet3BRouteTable 809 | DestinationCidrBlock: 0.0.0.0/0 810 | InstanceId: 811 | Fn::If: 812 | - NATInstanceCondition 813 | - Ref: NATInstance3 814 | - Ref: AWS::NoValue 815 | NatGatewayId: 816 | Fn::If: 817 | - NATGatewayCondition 818 | - Ref: NATGateway3 819 | - Ref: AWS::NoValue 820 | PrivateSubnet3BRouteTableAssociation: 821 | Condition: AdditionalPrivateSubnets&3AZCondition 822 | Type: AWS::EC2::SubnetRouteTableAssociation 823 | Properties: 824 | SubnetId: 825 | Ref: PrivateSubnet3B 826 | RouteTableId: 827 | Ref: PrivateSubnet3BRouteTable 828 | PrivateSubnet3BNetworkAcl: 829 | Condition: AdditionalPrivateSubnets&3AZCondition 830 | Type: AWS::EC2::NetworkAcl 831 | Properties: 832 | VpcId: 833 | Ref: VPC 834 | Tags: 835 | - Key: Name 836 | Value: NACL Protected subnet 3 837 | - Key: Network 838 | Value: NACL Protected 839 | PrivateSubnet3BNetworkAclEntryInbound: 840 | Condition: AdditionalPrivateSubnets&3AZCondition 841 | Type: AWS::EC2::NetworkAclEntry 842 | Properties: 843 | CidrBlock: 0.0.0.0/0 844 | Egress: 'false' 845 | NetworkAclId: 846 | Ref: PrivateSubnet3BNetworkAcl 847 | Protocol: "-1" 848 | RuleAction: allow 849 | RuleNumber: '100' 850 | PrivateSubnet3BNetworkAclEntryOutbound: 851 | Condition: AdditionalPrivateSubnets&3AZCondition 852 | Type: AWS::EC2::NetworkAclEntry 853 | Properties: 854 | CidrBlock: 0.0.0.0/0 855 | Egress: 'true' 856 | NetworkAclId: 857 | Ref: PrivateSubnet3BNetworkAcl 858 | Protocol: "-1" 859 | RuleAction: allow 860 | RuleNumber: '100' 861 | PrivateSubnet3BNetworkAclAssociation: 862 | Condition: AdditionalPrivateSubnets&3AZCondition 863 | Type: AWS::EC2::SubnetNetworkAclAssociation 864 | Properties: 865 | SubnetId: 866 | Ref: PrivateSubnet3B 867 | NetworkAclId: 868 | Ref: PrivateSubnet3BNetworkAcl 869 | PrivateSubnet4BRouteTable: 870 | Condition: AdditionalPrivateSubnets&4AZCondition 871 | Type: AWS::EC2::RouteTable 872 | Properties: 873 | VpcId: 874 | Ref: VPC 875 | Tags: 876 | - Key: Name 877 | Value: Private subnet 4B 878 | - Key: Network 879 | Value: Private 880 | PrivateSubnet4BRoute: 881 | Condition: AdditionalPrivateSubnets&4AZCondition 882 | Type: AWS::EC2::Route 883 | Properties: 884 | RouteTableId: 885 | Ref: PrivateSubnet4BRouteTable 886 | DestinationCidrBlock: 0.0.0.0/0 887 | InstanceId: 888 | Fn::If: 889 | - NATInstanceCondition 890 | - Ref: NATInstance4 891 | - Ref: AWS::NoValue 892 | NatGatewayId: 893 | Fn::If: 894 | - NATGatewayCondition 895 | - Ref: NATGateway4 896 | - Ref: AWS::NoValue 897 | PrivateSubnet4BRouteTableAssociation: 898 | Condition: AdditionalPrivateSubnets&4AZCondition 899 | Type: AWS::EC2::SubnetRouteTableAssociation 900 | Properties: 901 | SubnetId: 902 | Ref: PrivateSubnet4B 903 | RouteTableId: 904 | Ref: PrivateSubnet4BRouteTable 905 | PrivateSubnet4BNetworkAcl: 906 | Condition: AdditionalPrivateSubnets&4AZCondition 907 | Type: AWS::EC2::NetworkAcl 908 | Properties: 909 | VpcId: 910 | Ref: VPC 911 | Tags: 912 | - Key: Name 913 | Value: NACL Protected subnet 4 914 | - Key: Network 915 | Value: NACL Protected 916 | PrivateSubnet4BNetworkAclEntryInbound: 917 | Condition: AdditionalPrivateSubnets&4AZCondition 918 | Type: AWS::EC2::NetworkAclEntry 919 | Properties: 920 | CidrBlock: 0.0.0.0/0 921 | Egress: 'false' 922 | NetworkAclId: 923 | Ref: PrivateSubnet4BNetworkAcl 924 | Protocol: "-1" 925 | RuleAction: allow 926 | RuleNumber: '100' 927 | PrivateSubnet4BNetworkAclEntryOutbound: 928 | Condition: AdditionalPrivateSubnets&4AZCondition 929 | Type: AWS::EC2::NetworkAclEntry 930 | Properties: 931 | CidrBlock: 0.0.0.0/0 932 | Egress: 'true' 933 | NetworkAclId: 934 | Ref: PrivateSubnet4BNetworkAcl 935 | Protocol: "-1" 936 | RuleAction: allow 937 | RuleNumber: '100' 938 | PrivateSubnet4BNetworkAclAssociation: 939 | Condition: AdditionalPrivateSubnets&4AZCondition 940 | Type: AWS::EC2::SubnetNetworkAclAssociation 941 | Properties: 942 | SubnetId: 943 | Ref: PrivateSubnet4B 944 | NetworkAclId: 945 | Ref: PrivateSubnet4BNetworkAcl 946 | PublicSubnetRouteTable: 947 | Type: AWS::EC2::RouteTable 948 | Properties: 949 | VpcId: 950 | Ref: VPC 951 | Tags: 952 | - Key: Name 953 | Value: Public Subnets 954 | - Key: Network 955 | Value: Public 956 | PublicSubnetRoute: 957 | DependsOn: VPCGatewayAttachment 958 | Type: AWS::EC2::Route 959 | Properties: 960 | RouteTableId: 961 | Ref: PublicSubnetRouteTable 962 | DestinationCidrBlock: 0.0.0.0/0 963 | GatewayId: 964 | Ref: InternetGateway 965 | PublicSubnet1RouteTableAssociation: 966 | Type: AWS::EC2::SubnetRouteTableAssociation 967 | Properties: 968 | SubnetId: 969 | Ref: PublicSubnet1 970 | RouteTableId: 971 | Ref: PublicSubnetRouteTable 972 | PublicSubnet2RouteTableAssociation: 973 | Type: AWS::EC2::SubnetRouteTableAssociation 974 | Properties: 975 | SubnetId: 976 | Ref: PublicSubnet2 977 | RouteTableId: 978 | Ref: PublicSubnetRouteTable 979 | PublicSubnet3RouteTableAssociation: 980 | Condition: 3AZCondition 981 | Type: AWS::EC2::SubnetRouteTableAssociation 982 | Properties: 983 | SubnetId: 984 | Ref: PublicSubnet3 985 | RouteTableId: 986 | Ref: PublicSubnetRouteTable 987 | PublicSubnet4RouteTableAssociation: 988 | Condition: 4AZCondition 989 | Type: AWS::EC2::SubnetRouteTableAssociation 990 | Properties: 991 | SubnetId: 992 | Ref: PublicSubnet4 993 | RouteTableId: 994 | Ref: PublicSubnetRouteTable 995 | NAT1EIP: 996 | Condition: PrivateSubnetsCondition 997 | DependsOn: VPCGatewayAttachment 998 | Type: AWS::EC2::EIP 999 | Properties: 1000 | Domain: vpc 1001 | InstanceId: 1002 | Fn::If: 1003 | - NATInstanceCondition 1004 | - Ref: NATInstance1 1005 | - Ref: AWS::NoValue 1006 | NAT2EIP: 1007 | Condition: PrivateSubnetsCondition 1008 | DependsOn: VPCGatewayAttachment 1009 | Type: AWS::EC2::EIP 1010 | Properties: 1011 | Domain: vpc 1012 | InstanceId: 1013 | Fn::If: 1014 | - NATInstanceCondition 1015 | - Ref: NATInstance2 1016 | - Ref: AWS::NoValue 1017 | NAT3EIP: 1018 | Condition: PrivateSubnets&3AZCondition 1019 | DependsOn: VPCGatewayAttachment 1020 | Type: AWS::EC2::EIP 1021 | Properties: 1022 | Domain: vpc 1023 | InstanceId: 1024 | Fn::If: 1025 | - NATInstanceCondition 1026 | - Ref: NATInstance3 1027 | - Ref: AWS::NoValue 1028 | NAT4EIP: 1029 | Condition: PrivateSubnets&4AZCondition 1030 | DependsOn: VPCGatewayAttachment 1031 | Type: AWS::EC2::EIP 1032 | Properties: 1033 | Domain: vpc 1034 | InstanceId: 1035 | Fn::If: 1036 | - NATInstanceCondition 1037 | - Ref: NATInstance4 1038 | - Ref: AWS::NoValue 1039 | NATGateway1: 1040 | Condition: NATGatewayCondition 1041 | DependsOn: VPCGatewayAttachment 1042 | Type: AWS::EC2::NatGateway 1043 | Properties: 1044 | AllocationId: 1045 | Fn::GetAtt: 1046 | - NAT1EIP 1047 | - AllocationId 1048 | SubnetId: 1049 | Ref: PublicSubnet1 1050 | NATGateway2: 1051 | Condition: NATGatewayCondition 1052 | DependsOn: VPCGatewayAttachment 1053 | Type: AWS::EC2::NatGateway 1054 | Properties: 1055 | AllocationId: 1056 | Fn::GetAtt: 1057 | - NAT2EIP 1058 | - AllocationId 1059 | SubnetId: 1060 | Ref: PublicSubnet2 1061 | NATGateway3: 1062 | Condition: NATGateway&3AZCondition 1063 | DependsOn: VPCGatewayAttachment 1064 | Type: AWS::EC2::NatGateway 1065 | Properties: 1066 | AllocationId: 1067 | Fn::GetAtt: 1068 | - NAT3EIP 1069 | - AllocationId 1070 | SubnetId: 1071 | Ref: PublicSubnet3 1072 | NATGateway4: 1073 | Condition: NATGateway&4AZCondition 1074 | DependsOn: VPCGatewayAttachment 1075 | Type: AWS::EC2::NatGateway 1076 | Properties: 1077 | AllocationId: 1078 | Fn::GetAtt: 1079 | - NAT4EIP 1080 | - AllocationId 1081 | SubnetId: 1082 | Ref: PublicSubnet4 1083 | NATInstance1: 1084 | Condition: NATInstanceCondition 1085 | DependsOn: VPCGatewayAttachment 1086 | Type: AWS::EC2::Instance 1087 | Properties: 1088 | ImageId: 1089 | Fn::FindInMap: 1090 | - AWSAMIRegionMap 1091 | - Ref: AWS::Region 1092 | - AWSNATHVM 1093 | InstanceType: 1094 | Ref: NATInstanceType 1095 | Tags: 1096 | - Key: Name 1097 | Value: NAT1 1098 | NetworkInterfaces: 1099 | - GroupSet: 1100 | - Ref: NATInstanceSecurityGroup 1101 | AssociatePublicIpAddress: 'true' 1102 | DeviceIndex: '0' 1103 | DeleteOnTermination: 'true' 1104 | SubnetId: 1105 | Ref: PublicSubnet1 1106 | KeyName: 1107 | Fn::If: 1108 | - NATInstanceCondition 1109 | - Ref: KeyPairName 1110 | - Ref: AWS::NoValue 1111 | SourceDestCheck: 'false' 1112 | NATInstance2: 1113 | Condition: NATInstanceCondition 1114 | DependsOn: VPCGatewayAttachment 1115 | Type: AWS::EC2::Instance 1116 | Properties: 1117 | ImageId: 1118 | Fn::FindInMap: 1119 | - AWSAMIRegionMap 1120 | - Ref: AWS::Region 1121 | - AWSNATHVM 1122 | InstanceType: 1123 | Ref: NATInstanceType 1124 | Tags: 1125 | - Key: Name 1126 | Value: NAT2 1127 | NetworkInterfaces: 1128 | - GroupSet: 1129 | - Ref: NATInstanceSecurityGroup 1130 | AssociatePublicIpAddress: 'true' 1131 | DeviceIndex: '0' 1132 | DeleteOnTermination: 'true' 1133 | SubnetId: 1134 | Ref: PublicSubnet2 1135 | KeyName: 1136 | Fn::If: 1137 | - NATInstanceCondition 1138 | - Ref: KeyPairName 1139 | - Ref: AWS::NoValue 1140 | SourceDestCheck: 'false' 1141 | NATInstance3: 1142 | Condition: NATInstance&3AZCondition 1143 | DependsOn: VPCGatewayAttachment 1144 | Type: AWS::EC2::Instance 1145 | Properties: 1146 | ImageId: 1147 | Fn::FindInMap: 1148 | - AWSAMIRegionMap 1149 | - Ref: AWS::Region 1150 | - AWSNATHVM 1151 | InstanceType: 1152 | Ref: NATInstanceType 1153 | Tags: 1154 | - Key: Name 1155 | Value: NAT3 1156 | NetworkInterfaces: 1157 | - GroupSet: 1158 | - Ref: NATInstanceSecurityGroup 1159 | AssociatePublicIpAddress: 'true' 1160 | DeviceIndex: '0' 1161 | DeleteOnTermination: 'true' 1162 | SubnetId: 1163 | Ref: PublicSubnet3 1164 | KeyName: 1165 | Fn::If: 1166 | - NATInstanceCondition 1167 | - Ref: KeyPairName 1168 | - Ref: AWS::NoValue 1169 | SourceDestCheck: 'false' 1170 | NATInstance4: 1171 | Condition: NATInstance&4AZCondition 1172 | DependsOn: VPCGatewayAttachment 1173 | Type: AWS::EC2::Instance 1174 | Properties: 1175 | ImageId: 1176 | Fn::FindInMap: 1177 | - AWSAMIRegionMap 1178 | - Ref: AWS::Region 1179 | - AWSNATHVM 1180 | InstanceType: 1181 | Ref: NATInstanceType 1182 | Tags: 1183 | - Key: Name 1184 | Value: NAT4 1185 | NetworkInterfaces: 1186 | - GroupSet: 1187 | - Ref: NATInstanceSecurityGroup 1188 | AssociatePublicIpAddress: 'true' 1189 | DeviceIndex: '0' 1190 | DeleteOnTermination: 'true' 1191 | SubnetId: 1192 | Ref: PublicSubnet4 1193 | KeyName: 1194 | Fn::If: 1195 | - NATInstanceCondition 1196 | - Ref: KeyPairName 1197 | - Ref: AWS::NoValue 1198 | SourceDestCheck: 'false' 1199 | NATInstanceSecurityGroup: 1200 | Condition: NATInstanceCondition 1201 | Type: AWS::EC2::SecurityGroup 1202 | Properties: 1203 | GroupDescription: Enables outbound internet access for the VPC via the NAT instances 1204 | VpcId: 1205 | Ref: VPC 1206 | SecurityGroupIngress: 1207 | - IpProtocol: "-1" 1208 | FromPort: '1' 1209 | ToPort: '65535' 1210 | CidrIp: 1211 | Ref: VPCCIDR 1212 | S3VPCEndpoint: 1213 | Condition: S3VPCEndpointCondition 1214 | Type: AWS::EC2::VPCEndpoint 1215 | Properties: 1216 | PolicyDocument: 1217 | Version: '2012-10-17' 1218 | Statement: 1219 | - Action: "*" 1220 | Effect: Allow 1221 | Resource: "*" 1222 | Principal: "*" 1223 | RouteTableIds: 1224 | - Ref: PrivateSubnet1ARouteTable 1225 | - Ref: PrivateSubnet2ARouteTable 1226 | - Fn::If: 1227 | - PrivateSubnets&3AZCondition 1228 | - Ref: PrivateSubnet3ARouteTable 1229 | - Ref: AWS::NoValue 1230 | - Fn::If: 1231 | - PrivateSubnets&4AZCondition 1232 | - Ref: PrivateSubnet4ARouteTable 1233 | - Ref: AWS::NoValue 1234 | - Fn::If: 1235 | - AdditionalPrivateSubnetsCondition 1236 | - Ref: PrivateSubnet1BRouteTable 1237 | - Ref: AWS::NoValue 1238 | - Fn::If: 1239 | - AdditionalPrivateSubnetsCondition 1240 | - Ref: PrivateSubnet2BRouteTable 1241 | - Ref: AWS::NoValue 1242 | - Fn::If: 1243 | - AdditionalPrivateSubnets&3AZCondition 1244 | - Ref: PrivateSubnet3BRouteTable 1245 | - Ref: AWS::NoValue 1246 | - Fn::If: 1247 | - AdditionalPrivateSubnets&4AZCondition 1248 | - Ref: PrivateSubnet4BRouteTable 1249 | - Ref: AWS::NoValue 1250 | ServiceName: 1251 | Fn::Join: 1252 | - '' 1253 | - - com.amazonaws. 1254 | - Ref: AWS::Region 1255 | - ".s3" 1256 | VpcId: 1257 | Ref: VPC 1258 | Outputs: 1259 | NAT1EIP: 1260 | Condition: PrivateSubnetsCondition 1261 | Description: NAT 1 IP address 1262 | Value: 1263 | Ref: NAT1EIP 1264 | Export: 1265 | Name: 1266 | Fn::Sub: "${AWS::StackName}-NAT1EIP" 1267 | NAT2EIP: 1268 | Condition: PrivateSubnetsCondition 1269 | Description: NAT 2 IP address 1270 | Value: 1271 | Ref: NAT2EIP 1272 | Export: 1273 | Name: 1274 | Fn::Sub: "${AWS::StackName}-NAT2EIP" 1275 | NAT3EIP: 1276 | Condition: PrivateSubnets&3AZCondition 1277 | Description: NAT 3 IP address 1278 | Value: 1279 | Ref: NAT3EIP 1280 | Export: 1281 | Name: 1282 | Fn::Sub: "${AWS::StackName}-NAT3EIP" 1283 | NAT4EIP: 1284 | Condition: PrivateSubnets&4AZCondition 1285 | Description: NAT 4 IP address 1286 | Value: 1287 | Ref: NAT4EIP 1288 | Export: 1289 | Name: 1290 | Fn::Sub: "${AWS::StackName}-NAT4EIP" 1291 | PrivateSubnet1ACIDR: 1292 | Condition: PrivateSubnetsCondition 1293 | Description: Private subnet 1A CIDR in Availability Zone 1 1294 | Value: 1295 | Ref: PrivateSubnet1ACIDR 1296 | Export: 1297 | Name: 1298 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1ACIDR" 1299 | PrivateSubnet1AID: 1300 | Condition: PrivateSubnetsCondition 1301 | Description: Private subnet 1A ID in Availability Zone 1 1302 | Value: 1303 | Ref: PrivateSubnet1A 1304 | Export: 1305 | Name: 1306 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1AID" 1307 | PrivateSubnet1BCIDR: 1308 | Condition: AdditionalPrivateSubnetsCondition 1309 | Description: Private subnet 1B CIDR in Availability Zone 1 1310 | Value: 1311 | Ref: PrivateSubnet1BCIDR 1312 | Export: 1313 | Name: 1314 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1BCIDR" 1315 | PrivateSubnet1BID: 1316 | Condition: AdditionalPrivateSubnetsCondition 1317 | Description: Private subnet 1B ID in Availability Zone 1 1318 | Value: 1319 | Ref: PrivateSubnet1B 1320 | Export: 1321 | Name: 1322 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1BID" 1323 | PrivateSubnet2ACIDR: 1324 | Condition: PrivateSubnetsCondition 1325 | Description: Private subnet 2A CIDR in Availability Zone 2 1326 | Value: 1327 | Ref: PrivateSubnet2ACIDR 1328 | Export: 1329 | Name: 1330 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2ACIDR" 1331 | PrivateSubnet2AID: 1332 | Condition: PrivateSubnetsCondition 1333 | Description: Private subnet 2A ID in Availability Zone 2 1334 | Value: 1335 | Ref: PrivateSubnet2A 1336 | Export: 1337 | Name: 1338 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2AID" 1339 | PrivateSubnet2BCIDR: 1340 | Condition: AdditionalPrivateSubnetsCondition 1341 | Description: Private subnet 2B CIDR in Availability Zone 2 1342 | Value: 1343 | Ref: PrivateSubnet2BCIDR 1344 | Export: 1345 | Name: 1346 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2BCIDR" 1347 | PrivateSubnet2BID: 1348 | Condition: AdditionalPrivateSubnetsCondition 1349 | Description: Private subnet 2B ID in Availability Zone 2 1350 | Value: 1351 | Ref: PrivateSubnet2B 1352 | Export: 1353 | Name: 1354 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2BID" 1355 | PrivateSubnet3ACIDR: 1356 | Condition: PrivateSubnets&3AZCondition 1357 | Description: Private subnet 3A CIDR in Availability Zone 3 1358 | Value: 1359 | Ref: PrivateSubnet3ACIDR 1360 | Export: 1361 | Name: 1362 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3ACIDR" 1363 | PrivateSubnet3AID: 1364 | Condition: PrivateSubnets&3AZCondition 1365 | Description: Private subnet 3A ID in Availability Zone 3 1366 | Value: 1367 | Ref: PrivateSubnet3A 1368 | Export: 1369 | Name: 1370 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3AID" 1371 | PrivateSubnet3BCIDR: 1372 | Condition: AdditionalPrivateSubnets&3AZCondition 1373 | Description: Private subnet 3B CIDR in Availability Zone 3 1374 | Value: 1375 | Ref: PrivateSubnet3BCIDR 1376 | Export: 1377 | Name: 1378 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3BCIDR" 1379 | PrivateSubnet3BID: 1380 | Condition: AdditionalPrivateSubnets&3AZCondition 1381 | Description: Private subnet 3B ID in Availability Zone 3 1382 | Value: 1383 | Ref: PrivateSubnet3B 1384 | Export: 1385 | Name: 1386 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3BID" 1387 | PrivateSubnet4ACIDR: 1388 | Condition: PrivateSubnets&4AZCondition 1389 | Description: Private subnet 4A CIDR in Availability Zone 4 1390 | Value: 1391 | Ref: PrivateSubnet4ACIDR 1392 | Export: 1393 | Name: 1394 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4ACIDR" 1395 | PrivateSubnet4AID: 1396 | Condition: PrivateSubnets&4AZCondition 1397 | Description: Private subnet 4A ID in Availability Zone 4 1398 | Value: 1399 | Ref: PrivateSubnet4A 1400 | Export: 1401 | Name: 1402 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4AID" 1403 | PrivateSubnet4BCIDR: 1404 | Condition: AdditionalPrivateSubnets&4AZCondition 1405 | Description: Private subnet 4B CIDR in Availability Zone 4 1406 | Value: 1407 | Ref: PrivateSubnet4BCIDR 1408 | Export: 1409 | Name: 1410 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4BCIDR" 1411 | PrivateSubnet4BID: 1412 | Condition: AdditionalPrivateSubnets&4AZCondition 1413 | Description: Private subnet 4B ID in Availability Zone 4 1414 | Value: 1415 | Ref: PrivateSubnet4B 1416 | Export: 1417 | Name: 1418 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4BID" 1419 | PublicSubnet1CIDR: 1420 | Description: Public subnet 1 CIDR in Availability Zone 1 1421 | Value: 1422 | Ref: PublicSubnet1CIDR 1423 | Export: 1424 | Name: 1425 | Fn::Sub: "${AWS::StackName}-PublicSubnet1CIDR" 1426 | PublicSubnet1ID: 1427 | Description: Public subnet 1 ID in Availability Zone 1 1428 | Value: 1429 | Ref: PublicSubnet1 1430 | Export: 1431 | Name: 1432 | Fn::Sub: "${AWS::StackName}-PublicSubnet1ID" 1433 | PublicSubnet2CIDR: 1434 | Description: Public subnet 2 CIDR in Availability Zone 2 1435 | Value: 1436 | Ref: PublicSubnet2CIDR 1437 | Export: 1438 | Name: 1439 | Fn::Sub: "${AWS::StackName}-PublicSubnet2CIDR" 1440 | PublicSubnet2ID: 1441 | Description: Public subnet 2 ID in Availability Zone 2 1442 | Value: 1443 | Ref: PublicSubnet2 1444 | Export: 1445 | Name: 1446 | Fn::Sub: "${AWS::StackName}-PublicSubnet2ID" 1447 | PublicSubnet3CIDR: 1448 | Condition: 3AZCondition 1449 | Description: Public subnet 3 CIDR in Availability Zone 3 1450 | Value: 1451 | Ref: PublicSubnet3CIDR 1452 | Export: 1453 | Name: 1454 | Fn::Sub: "${AWS::StackName}-PublicSubnet3CIDR" 1455 | PublicSubnet3ID: 1456 | Condition: 3AZCondition 1457 | Description: Public subnet 3 ID in Availability Zone 3 1458 | Value: 1459 | Ref: PublicSubnet3 1460 | Export: 1461 | Name: 1462 | Fn::Sub: "${AWS::StackName}-PublicSubnet3ID" 1463 | PublicSubnet4CIDR: 1464 | Condition: 4AZCondition 1465 | Description: Public subnet 4 CIDR in Availability Zone 4 1466 | Value: 1467 | Ref: PublicSubnet4CIDR 1468 | Export: 1469 | Name: 1470 | Fn::Sub: "${AWS::StackName}-PublicSubnet4CIDR" 1471 | PublicSubnet4ID: 1472 | Condition: 4AZCondition 1473 | Description: Public subnet 4 ID in Availability Zone 4 1474 | Value: 1475 | Ref: PublicSubnet4 1476 | Export: 1477 | Name: 1478 | Fn::Sub: "${AWS::StackName}-PublicSubnet4ID" 1479 | S3VPCEndpoint: 1480 | Condition: S3VPCEndpointCondition 1481 | Description: S3 VPC Endpoint 1482 | Value: 1483 | Ref: S3VPCEndpoint 1484 | Export: 1485 | Name: 1486 | Fn::Sub: "${AWS::StackName}-S3VPCEndpoint" 1487 | PrivateSubnet1ARouteTable: 1488 | Condition: PrivateSubnetsCondition 1489 | Value: 1490 | Ref: PrivateSubnet1ARouteTable 1491 | Description: Private subnet 1A route table 1492 | Export: 1493 | Name: 1494 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1ARouteTable" 1495 | PrivateSubnet1BRouteTable: 1496 | Condition: AdditionalPrivateSubnetsCondition 1497 | Value: 1498 | Ref: PrivateSubnet1BRouteTable 1499 | Description: Private subnet 1B route table 1500 | Export: 1501 | Name: 1502 | Fn::Sub: "${AWS::StackName}-PrivateSubnet1BRouteTable" 1503 | PrivateSubnet2ARouteTable: 1504 | Condition: PrivateSubnetsCondition 1505 | Value: 1506 | Ref: PrivateSubnet2ARouteTable 1507 | Description: Private subnet 2A route table 1508 | Export: 1509 | Name: 1510 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2ARouteTable" 1511 | PrivateSubnet2BRouteTable: 1512 | Condition: AdditionalPrivateSubnetsCondition 1513 | Value: 1514 | Ref: PrivateSubnet2BRouteTable 1515 | Description: Private subnet 2B route table 1516 | Export: 1517 | Name: 1518 | Fn::Sub: "${AWS::StackName}-PrivateSubnet2BRouteTable" 1519 | PrivateSubnet3ARouteTable: 1520 | Condition: PrivateSubnets&3AZCondition 1521 | Value: 1522 | Ref: PrivateSubnet3ARouteTable 1523 | Description: Private subnet 3A route table 1524 | Export: 1525 | Name: 1526 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3ARouteTable" 1527 | PrivateSubnet3BRouteTable: 1528 | Condition: AdditionalPrivateSubnets&3AZCondition 1529 | Value: 1530 | Ref: PrivateSubnet3BRouteTable 1531 | Description: Private subnet 3B route table 1532 | Export: 1533 | Name: 1534 | Fn::Sub: "${AWS::StackName}-PrivateSubnet3BRouteTable" 1535 | PrivateSubnet4ARouteTable: 1536 | Condition: PrivateSubnets&4AZCondition 1537 | Value: 1538 | Ref: PrivateSubnet4ARouteTable 1539 | Description: Private subnet 4A route table 1540 | Export: 1541 | Name: 1542 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4ARouteTable" 1543 | PrivateSubnet4BRouteTable: 1544 | Condition: AdditionalPrivateSubnets&4AZCondition 1545 | Value: 1546 | Ref: PrivateSubnet4BRouteTable 1547 | Description: Private subnet 4B route table 1548 | Export: 1549 | Name: 1550 | Fn::Sub: "${AWS::StackName}-PrivateSubnet4BRouteTable" 1551 | PublicSubnetRouteTable: 1552 | Value: 1553 | Ref: PublicSubnetRouteTable 1554 | Description: Public subnet route table 1555 | Export: 1556 | Name: 1557 | Fn::Sub: "${AWS::StackName}-PublicSubnetRouteTable" 1558 | VPCCIDR: 1559 | Value: 1560 | Ref: VPCCIDR 1561 | Description: VPC CIDR 1562 | Export: 1563 | Name: 1564 | Fn::Sub: "${AWS::StackName}-VPCCIDR" 1565 | VPCID: 1566 | Value: 1567 | Ref: VPC 1568 | Description: VPC ID 1569 | Export: 1570 | Name: 1571 | Fn::Sub: "${AWS::StackName}-VPCID" 1572 | AvailabilityZone1: 1573 | Value: 1574 | Fn::Select: 1575 | - '0' 1576 | - Ref: AvailabilityZones 1577 | Export: 1578 | Name: !Sub '${AWS::StackName}-AvailabilityZone1' 1579 | AvailabilityZone2: 1580 | Value: 1581 | Fn::Select: 1582 | - '1' 1583 | - Ref: AvailabilityZones 1584 | Export: 1585 | Name: !Sub '${AWS::StackName}-AvailabilityZone2' 1586 | -------------------------------------------------------------------------------- /wordpress/_cim.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | stack: 3 | name: headless-wp-service # Note: Update this with your stack name 4 | template: 5 | file: wp.stack.yml 6 | bucket: cim-stack-artifacts # Note: Update this with your bucket name. Stacks are uploaded here prior to deployment.' 7 | 8 | # 9 | # Define stack input parameters. 10 | # 11 | parameters: 12 | VPCStack: 'headless-wp-vpc' 13 | ECSStack: 'headless-wp-ecs' 14 | RDSStack: 'headless-wp-rds' 15 | ECRStack: 'headless-wp-ecr' 16 | Version: '1.0.0' 17 | Path: '/*' 18 | TLD: 'bluefineng.com' 19 | Domain: 'cdn.bluefineng.com' 20 | SSL: 'arn:aws:acm:us-east-1:132093761664:certificate/7a872cf1-4845-46f5-9a05-6183bafad0c2' 21 | 22 | # 23 | # Define stack capabilities required. 24 | # 25 | capabilities: 26 | - 'CAPABILITY_NAMED_IAM' 27 | 28 | # 29 | # Define global tags. 30 | # 31 | tags: 32 | app: 'headless-wp' 33 | -------------------------------------------------------------------------------- /wordpress/src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM wordpress:latest 2 | 3 | # Install unzip 4 | RUN apt-get update; \ 5 | apt-get install -y --no-install-recommends unzip 6 | 7 | # Install WP plugins 8 | RUN curl -L https://downloads.wordpress.org/plugin/amazon-s3-and-cloudfront.2.0.zip -o /tmp/amazon-s3-and-cloudfront.2.0.zip 9 | RUN unzip /tmp/amazon-s3-and-cloudfront.2.0.zip -d /usr/src/wordpress/wp-content/plugins 10 | RUN rm /tmp/amazon-s3-and-cloudfront.2.0.zip 11 | 12 | RUN curl -L https://downloads.wordpress.org/plugin/advanced-custom-fields.5.7.7.zip -o /tmp/advanced-custom-fields.5.7.7.zip 13 | RUN unzip /tmp/advanced-custom-fields.5.7.7.zip -d /usr/src/wordpress/wp-content/plugins 14 | RUN rm /tmp/advanced-custom-fields.5.7.7.zip 15 | 16 | RUN curl -L https://downloads.wordpress.org/plugin/custom-post-type-ui.1.5.8.zip -o /tmp/custom-post-type-ui.1.5.8.zip 17 | RUN unzip /tmp/custom-post-type-ui.1.5.8.zip -d /usr/src/wordpress/wp-content/plugins 18 | RUN rm /tmp/custom-post-type-ui.1.5.8.zip -------------------------------------------------------------------------------- /wordpress/src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | 5 | headless_wordpress: 6 | build: . 7 | #image: wordpress 8 | restart: always 9 | ports: 10 | - 8080:80 11 | environment: 12 | WORDPRESS_DB_PASSWORD: example 13 | WORDPRESS_ADDITIONAL_CONFIG: | 14 | define( 'AS3CF_AWS_USE_EC2_IAM_ROLE', true ); 15 | 16 | mysql: 17 | image: mysql:5.7 18 | restart: always 19 | environment: 20 | MYSQL_ROOT_PASSWORD: example -------------------------------------------------------------------------------- /wordpress/wp.stack.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Parameters: 3 | VPCStack: 4 | Type: String 5 | Description: VPC Stack Name 6 | ECSStack: 7 | Type: String 8 | Description: ECS Stack Name 9 | ECRStack: 10 | Type: String 11 | Description: ECR Stack Name 12 | RDSStack: 13 | Type: String 14 | Description: RDS Stack Name 15 | DesiredCount: 16 | Type: Number 17 | Default: '2' 18 | Description: Desired task count 19 | Path: 20 | Type: String 21 | Default: '/service1' 22 | Description: Service path 23 | Version: 24 | Type: String 25 | Default: 'latest' 26 | Description: Service version 27 | 28 | ListenerRulePriority: 29 | Type: Number 30 | Default: 2 31 | 32 | TLD: 33 | Type: String 34 | Description: TLD name needed by Route53 to perform DNS (example.com) 35 | Default: '' 36 | 37 | Domain: 38 | Type: String 39 | Description: Domain name for your api (api.example.com) 40 | Default: '' 41 | 42 | SSL: 43 | Type: String 44 | Description: SSL Arn for your Domain 45 | Default: '' 46 | 47 | Conditions: 48 | UseCustomDomain: !And 49 | - !Not [!Equals [!Ref TLD, '']] 50 | - !Not [!Equals [!Ref Domain, '']] 51 | - !Not [!Equals [!Ref SSL, '']] 52 | 53 | Resources: 54 | 55 | # 56 | # Wordpress media files bucket 57 | # 58 | MediaBucket: 59 | Type: AWS::S3::Bucket 60 | Properties: 61 | AccessControl: PublicRead 62 | WebsiteConfiguration: 63 | IndexDocument: 'index.html' 64 | ErrorDocument: '404.html' 65 | 66 | # 67 | # CloudFront CDN 68 | # 69 | CDN: 70 | Type: AWS::CloudFront::Distribution 71 | DependsOn: MediaBucket 72 | Properties: 73 | DistributionConfig: 74 | Aliases: !If [UseCustomDomain, [!Ref Domain], !Ref "AWS::NoValue"] 75 | Enabled: true 76 | PriceClass: 'PriceClass_All' 77 | DefaultCacheBehavior: 78 | TargetOriginId: !Ref MediaBucket 79 | ViewerProtocolPolicy: !If [UseCustomDomain, "redirect-to-https", "allow-all"] 80 | MinTTL: 0 81 | AllowedMethods: 82 | - 'HEAD' 83 | - 'GET' 84 | CachedMethods: 85 | - 'HEAD' 86 | - 'GET' 87 | ForwardedValues: 88 | QueryString: false 89 | Cookies: 90 | Forward: none 91 | CacheBehaviors: 92 | - 93 | TargetOriginId: !Ref MediaBucket 94 | PathPattern: '/wp-content/*' 95 | ViewerProtocolPolicy: !If [UseCustomDomain, "redirect-to-https", "allow-all"] 96 | AllowedMethods: 97 | - 'HEAD' 98 | - 'GET' 99 | CachedMethods: 100 | - 'HEAD' 101 | - 'GET' 102 | ForwardedValues: 103 | QueryString: false 104 | Cookies: 105 | Forward: none 106 | - 107 | TargetOriginId: 'wp-alb' 108 | PathPattern: '/wp-json/*' 109 | MaxTTL: 60000 110 | DefaultTTL: 10000 111 | ViewerProtocolPolicy: !If [UseCustomDomain, "redirect-to-https", "allow-all"] 112 | AllowedMethods: 113 | - 'HEAD' 114 | - 'GET' 115 | CachedMethods: 116 | - 'HEAD' 117 | - 'GET' 118 | ForwardedValues: 119 | QueryString: false 120 | Cookies: 121 | Forward: none 122 | Origins: 123 | - 124 | Id: !Ref MediaBucket 125 | DomainName: !Sub '${MediaBucket}.s3-website-${AWS::Region}.amazonaws.com' 126 | CustomOriginConfig: 127 | HTTPPort: 80 128 | HTTPSPort: 443 129 | OriginProtocolPolicy: 'http-only' 130 | - 131 | Id: 'wp-alb' 132 | DomainName: 133 | Fn::ImportValue: 134 | !Sub "${ECSStack}-LoadBalancerUrl" 135 | CustomOriginConfig: 136 | HTTPPort: 80 137 | HTTPSPort: 443 138 | OriginProtocolPolicy: !If [UseCustomDomain, "https-only", "http-only"] 139 | Restrictions: 140 | GeoRestriction: 141 | RestrictionType: 'none' 142 | ViewerCertificate: 143 | SslSupportMethod: !If [UseCustomDomain, "sni-only", !Ref "AWS::NoValue"] 144 | MinimumProtocolVersion: !If [UseCustomDomain, "TLSv1", !Ref "AWS::NoValue"] 145 | AcmCertificateArn: !If [UseCustomDomain, !Ref SSL, !Ref "AWS::NoValue"] 146 | CloudFrontDefaultCertificate: !If [UseCustomDomain, !Ref "AWS::NoValue", true] 147 | 148 | # 149 | # Route53 DNS record set to map our domain to our CDN 150 | # 151 | DomainDNS: 152 | Type: AWS::Route53::RecordSetGroup 153 | Condition: UseCustomDomain 154 | Properties: 155 | HostedZoneName: !Sub '${TLD}.' 156 | RecordSets: 157 | - 158 | Name: !Ref Domain 159 | Type: 'A' 160 | AliasTarget: 161 | HostedZoneId: 'Z2FDTNDATAQYW2' # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html#cfn-route53-aliastarget-hostedzoneid 162 | DNSName: !GetAtt CDN.DomainName 163 | 164 | CloudWatchLogsGroup: 165 | Type: AWS::Logs::LogGroup 166 | Properties: 167 | LogGroupName: !Ref AWS::StackName 168 | RetentionInDays: 365 169 | 170 | # This IAM Role grants the service access to register/unregister with the 171 | # Application Load Balancer (ALB). It is based on the default documented here: 172 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_IAM_role.html 173 | ServiceRole: 174 | Type: AWS::IAM::Role 175 | Properties: 176 | RoleName: !Sub ecs-service-${AWS::StackName} 177 | Path: / 178 | AssumeRolePolicyDocument: | 179 | { 180 | "Statement": [{ 181 | "Effect": "Allow", 182 | "Principal": { "Service": [ "ecs.amazonaws.com" ]}, 183 | "Action": [ "sts:AssumeRole" ] 184 | }] 185 | } 186 | Policies: 187 | - PolicyName: !Sub ecs-service-${AWS::StackName} 188 | PolicyDocument: 189 | { 190 | "Version": "2012-10-17", 191 | "Statement": [{ 192 | "Effect": "Allow", 193 | "Action": [ 194 | "ec2:AuthorizeSecurityGroupIngress", 195 | "ec2:Describe*", 196 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 197 | "elasticloadbalancing:Describe*", 198 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 199 | "elasticloadbalancing:DeregisterTargets", 200 | "elasticloadbalancing:DescribeTargetGroups", 201 | "elasticloadbalancing:DescribeTargetHealth", 202 | "elasticloadbalancing:RegisterTargets" 203 | ], 204 | "Resource": "*" 205 | }] 206 | } 207 | 208 | TaskRole: 209 | Type: AWS::IAM::Role 210 | Properties: 211 | RoleName: !Sub ecs-task-${AWS::StackName} 212 | Path: / 213 | AssumeRolePolicyDocument: 214 | Version: 2012-10-17 215 | Statement: 216 | Effect: Allow 217 | Principal: 218 | Service: 219 | - ecs-tasks.amazonaws.com 220 | Action: sts:AssumeRole 221 | Policies: 222 | - PolicyName: !Sub ecs-service-${AWS::StackName} 223 | PolicyDocument: 224 | Version: 2012-10-17 225 | Statement: 226 | - Effect: Allow 227 | Action: 228 | - s3:* 229 | Resource: 230 | - !Sub 'arn:aws:s3:::${MediaBucket}' 231 | - !Sub 'arn:aws:s3:::${MediaBucket}/*' 232 | 233 | TaskDefinition: 234 | Type: AWS::ECS::TaskDefinition 235 | DependsOn: 236 | - CDN 237 | - MediaBucket 238 | Properties: 239 | Family: !Ref AWS::StackName 240 | TaskRoleArn: !GetAtt TaskRole.Arn 241 | ContainerDefinitions: 242 | - Name: !Ref AWS::StackName 243 | Essential: true 244 | Image: !Sub 245 | - "${URL}:${Version}" 246 | - { 247 | URL: { "Fn::ImportValue" : {"Fn::Sub": "${ECRStack}-Service1RepositoryUrl" } }, 248 | Version: !Ref Version 249 | } 250 | Memory: 128 251 | PortMappings: 252 | - ContainerPort: 80 253 | LogConfiguration: 254 | LogDriver: awslogs 255 | Options: 256 | awslogs-group: !Ref AWS::StackName 257 | awslogs-region: !Ref AWS::Region 258 | Environment: 259 | - Name: AWS_REGION 260 | Value: !Ref AWS::Region 261 | - Name: AWS_ACCOUNT_ID 262 | Value: !Ref AWS::AccountId 263 | - Name: WORDPRESS_DB_HOST 264 | Value: 265 | Fn::ImportValue: 266 | !Sub "${RDSStack}-RDSClusterEndpoint" 267 | - Name: WORDPRESS_DB_USER 268 | Value: 269 | Fn::ImportValue: 270 | !Sub "${RDSStack}-DatabaseUsername" 271 | - Name: WORDPRESS_DB_PASSWORD 272 | Value: 273 | Fn::ImportValue: 274 | !Sub "${RDSStack}-DatabasePassword" 275 | - Name: WORDPRESS_CONFIG_EXTRA 276 | Value: !Sub 277 | - | 278 | define( 'AS3CF_AWS_USE_EC2_IAM_ROLE', true ); 279 | define( 'AS3CF_SETTINGS', serialize( array( 280 | 'bucket' => '${MediaBucket}', 281 | 'copy-to-s3' => true, 282 | 'serve-from-s3' => true, 283 | 'domain' => 'cloudfront', 284 | 'cloudfront' => '${DomainName}', 285 | 'enable-object-prefix' => true, 286 | 'object-prefix' => 'wp-content/uploads/', 287 | 'force-https' => ${ForceHttps}, 288 | 'remove-local-file' => true 289 | ) ) ); 290 | define( 'WP_HOME', '${CMSUrl}' ); 291 | define( 'WP_SITEURL', '${CMSUrl}' ); 292 | - { 293 | MediaBucket: !Ref MediaBucket, 294 | DomainName: !If [UseCustomDomain, !Ref Domain, !GetAtt CDN.DomainName], 295 | ForceHttps: !If [UseCustomDomain, 'true', 'false'], 296 | CMSUrl: !If [UseCustomDomain, { "Fn::ImportValue" : {"Fn::Sub": "${ECSStack}-CustomDomainUrl" } }, {"Fn::Sub": ["http://${Url}", {"Url": { "Fn::ImportValue" : {"Fn::Sub": "${ECSStack}-LoadBalancerUrl" } }}]}] 297 | } 298 | - Name: WORDPRESS_DEBUG 299 | Value: '1' 300 | 301 | TargetGroup: 302 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 303 | Properties: 304 | VpcId: 305 | Fn::ImportValue: 306 | !Sub "${VPCStack}-VPCID" 307 | Port: 80 308 | Protocol: HTTP 309 | Matcher: 310 | HttpCode: 200-399 311 | HealthCheckIntervalSeconds: 10 312 | HealthCheckPath: '/' 313 | HealthCheckProtocol: HTTP 314 | HealthCheckTimeoutSeconds: 5 315 | HealthyThresholdCount: 2 316 | TargetGroupAttributes: 317 | - Key: 'stickiness.enabled' 318 | Value: 'true' 319 | - Key: 'stickiness.type' 320 | Value: 'lb_cookie' 321 | 322 | ListenerRuleHttp: 323 | Type: AWS::ElasticLoadBalancingV2::ListenerRule 324 | Properties: 325 | ListenerArn: 326 | Fn::ImportValue: 327 | !Sub "${ECSStack}-LoadBalancerListenerHttp" 328 | Priority: !Ref ListenerRulePriority 329 | Conditions: 330 | - Field: path-pattern 331 | Values: 332 | - !Ref Path 333 | Actions: 334 | - TargetGroupArn: !Ref TargetGroup 335 | Type: forward 336 | 337 | ListenerRuleHttps: 338 | Type: AWS::ElasticLoadBalancingV2::ListenerRule 339 | Condition: UseCustomDomain 340 | Properties: 341 | ListenerArn: 342 | Fn::ImportValue: 343 | !Sub "${ECSStack}-LoadBalancerListenerHttps" 344 | Priority: !Ref ListenerRulePriority 345 | Conditions: 346 | - Field: path-pattern 347 | Values: 348 | - !Ref Path 349 | Actions: 350 | - TargetGroupArn: !Ref TargetGroup 351 | Type: forward 352 | 353 | Service: 354 | Type: AWS::ECS::Service 355 | Properties: 356 | Cluster: 357 | Fn::ImportValue: 358 | !Sub "${ECSStack}-ECSCluster" 359 | Role: !Ref ServiceRole 360 | DesiredCount: !Ref DesiredCount 361 | TaskDefinition: !Ref TaskDefinition 362 | DeploymentConfiguration: 363 | MinimumHealthyPercent: 50 364 | LoadBalancers: 365 | - ContainerName: !Ref AWS::StackName 366 | ContainerPort: 80 367 | TargetGroupArn: !Ref TargetGroup 368 | 369 | Outputs: 370 | Service: 371 | Value: !Ref 'Service' 372 | Export: 373 | Name: !Sub '${AWS::StackName}-Service' 374 | TaskDefinition: 375 | Value: !Ref 'TaskDefinition' 376 | Export: 377 | Name: !Sub '${AWS::StackName}-TaskDefinition' 378 | CloudWatchLogsGroup: 379 | Value: !Ref 'CloudWatchLogsGroup' 380 | Export: 381 | Name: !Sub '${AWS::StackName}-CloudWatchLogsGroup' 382 | CDNUrl: 383 | Value: !If [UseCustomDomain, !Ref Domain, !GetAtt CDN.DomainName] 384 | Export: 385 | Name: !Sub '${AWS::StackName}-CDNUrl' 386 | CDN: 387 | Value: !Ref CDN 388 | Export: 389 | Name: !Sub '${AWS::StackName}-CDN' --------------------------------------------------------------------------------