├── .gitignore ├── LICENSE ├── README.md ├── application ├── .gitignore ├── composer.json ├── composer.lock └── index.php ├── bitbucket-pipelines.yml ├── docker-compose.yml └── environment ├── artifact ├── .ebextensions │ └── post_deploy.config └── Dockerrun.aws.json ├── containers ├── development │ └── eb_single_php_container_app │ │ ├── Dockerfile │ │ └── apache-config.conf └── production │ └── eb_single_php_container_app │ ├── Dockerfile │ └── apache-config.conf └── infrastructure ├── application ├── README.md ├── backend.tf ├── beanstalk_environment.tf ├── config │ ├── production.tfvars │ └── staging.tfvars ├── database.tf ├── network.tf ├── provider.tf └── versions.tf └── global ├── README.md ├── backend.tf ├── beanstalk_applications.tf ├── container_registry.tf ├── provider.tf ├── s3_bucket.tf ├── ssh_key.tf ├── terraform.tfvars ├── versions.tf ├── vpc-production.tf └── vpc-staging.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea/* 3 | 4 | # Terraform 5 | .terraform 6 | 7 | # Docker 8 | /logs 9 | .docker.env 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jonathan 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform and Bitbucket Pipelines Example 2 | This is an example using terraform to deploy AWS infrastructure (Beanstalk) and Bitbucket pipelines to build and deploy a docker image with AWS ECR. 3 | 4 | ## Terraform 5 | 6 | In this example Terraform is spill up into global and application. 7 | 8 | ### Global 9 | 10 | Global handles infrastructure that is shared between multiple applications or environments. 11 | 12 | Example 13 | - A Production VPC mite hold three different websites that are managed in three separate repos. 14 | - Or ECR which is used for one application but all environments (production and staging) 15 | 16 | Global is run once overall and will setup: 17 | 18 | - Beanstalk application for "EB Single Container App" 19 | - ECR (Docker Registry) 20 | - S3 Bucket 21 | - SSH Key 22 | - VPC for Production environments 23 | - VPC for Staging environments 24 | 25 | Path: `environment/infrastructure/global`. 26 | 27 | ### Application 28 | 29 | Application handles infrastructure related only to that applications environment. 30 | 31 | Example 32 | - Each application and environment will have its own database 33 | - Each application and environment will have its own Beanstalk instance 34 | 35 | Application is run for each environment and will setup for each environment: 36 | 37 | - Beanstalk Environment 38 | - MariaDB Instance 39 | 40 | Path: `environment/infrastructure/application` 41 | 42 | ### Setup 43 | 44 | - Run terraform commands* for Global. 45 | - Get the S3 bucket name for the builds bucket and put into application tfvar files for beanstalk tag `BUILD_CONFIG_BUCKET` 46 | - Run terraform commands* for each Application environment. 47 | 48 | *See readme in global/application folder for commands. 49 | 50 | ## Bitbucket Pipelines 51 | 52 | ### Process 53 | 54 | The pipelines steps/process in this example will: 55 | 56 | - Run `composer install` within the application directory 57 | - Build a docker image and push it to AWS ECR based on the config in `environment/containers/production/eb_single_php_container_app` 58 | - Make a Beanstalk version, upload it to S3 and register with Beanstalk using the configs in `environment/artifact` 59 | - Allow you to deploy to Staging environment, then to Production 60 | 61 | ### Require Environment Variables 62 | 63 | Required environment variables that need to be added in BitBucket for the pipelines to run correctly. 64 | 65 | The AWS Access Key used here will require write access to a bucket to store Beanstalk versions and read/write access to AWS ECR. 66 | 67 | |Name|Description|Example| 68 | |----|-----------|-------| 69 | |AWS_ACCESS_KEY_ID|AWS access key associated with an IAM user or role.|AKIAIOSFODNN7EXAMPLE| 70 | |AWS_SECRET_ACCESS_KEY|Secret key associated with the access key.|wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY| 71 | |AWS_DEFAULT_REGION|AWS Region to send the request to.|ap-southeast-2| 72 | |DOCKER_IMAGE_URL|AWS ECR URI for your image|111111111111.dkr.ecr.ap-southeast-2.amazonaws.com/sct| 73 | |BUILD_BUCKET|S3 Bucket name where builds will be stored|ct-builds-configs20190605042428563200000001| 74 | |BUILD_PATH|Path in S3 Bucket to store builds|builds/sct| 75 | |APP_NAME|AWS Beanstalk application name to register version with|EB Single Container App| 76 | |APP_ENV_STAGING|AWS Beanstalk application environment name for Staging|eb-single-container-app-staging| 77 | |APP_ENV_PRODUCTION|AWS Beanstalk application environment name for Production|eb-single-container-app-production| 78 | 79 | ## Local Setup 80 | 81 | - Setup [Traefik](https://hub.docker.com/_/traefik) 82 | - Run `docker-compose up -d` 83 | - Add host hack (127.0.0.1 local.eb.test.com) -------------------------------------------------------------------------------- /application/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | -------------------------------------------------------------------------------- /application/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "vlucas/phpdotenv": "^3.4" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /application/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c402b2825332d403bd2047101632d3e6", 8 | "packages": [ 9 | { 10 | "name": "phpoption/phpoption", 11 | "version": "1.5.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/schmittjoh/php-option.git", 15 | "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", 20 | "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "4.7.*" 28 | }, 29 | "type": "library", 30 | "extra": { 31 | "branch-alias": { 32 | "dev-master": "1.3-dev" 33 | } 34 | }, 35 | "autoload": { 36 | "psr-0": { 37 | "PhpOption\\": "src/" 38 | } 39 | }, 40 | "notification-url": "https://packagist.org/downloads/", 41 | "license": [ 42 | "Apache2" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Johannes M. Schmitt", 47 | "email": "schmittjoh@gmail.com" 48 | } 49 | ], 50 | "description": "Option Type for PHP", 51 | "keywords": [ 52 | "language", 53 | "option", 54 | "php", 55 | "type" 56 | ], 57 | "time": "2015-07-25T16:39:46+00:00" 58 | }, 59 | { 60 | "name": "symfony/polyfill-ctype", 61 | "version": "v1.11.0", 62 | "source": { 63 | "type": "git", 64 | "url": "https://github.com/symfony/polyfill-ctype.git", 65 | "reference": "82ebae02209c21113908c229e9883c419720738a" 66 | }, 67 | "dist": { 68 | "type": "zip", 69 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", 70 | "reference": "82ebae02209c21113908c229e9883c419720738a", 71 | "shasum": "" 72 | }, 73 | "require": { 74 | "php": ">=5.3.3" 75 | }, 76 | "suggest": { 77 | "ext-ctype": "For best performance" 78 | }, 79 | "type": "library", 80 | "extra": { 81 | "branch-alias": { 82 | "dev-master": "1.11-dev" 83 | } 84 | }, 85 | "autoload": { 86 | "psr-4": { 87 | "Symfony\\Polyfill\\Ctype\\": "" 88 | }, 89 | "files": [ 90 | "bootstrap.php" 91 | ] 92 | }, 93 | "notification-url": "https://packagist.org/downloads/", 94 | "license": [ 95 | "MIT" 96 | ], 97 | "authors": [ 98 | { 99 | "name": "Symfony Community", 100 | "homepage": "https://symfony.com/contributors" 101 | }, 102 | { 103 | "name": "Gert de Pagter", 104 | "email": "BackEndTea@gmail.com" 105 | } 106 | ], 107 | "description": "Symfony polyfill for ctype functions", 108 | "homepage": "https://symfony.com", 109 | "keywords": [ 110 | "compatibility", 111 | "ctype", 112 | "polyfill", 113 | "portable" 114 | ], 115 | "time": "2019-02-06T07:57:58+00:00" 116 | }, 117 | { 118 | "name": "vlucas/phpdotenv", 119 | "version": "v3.4.0", 120 | "source": { 121 | "type": "git", 122 | "url": "https://github.com/vlucas/phpdotenv.git", 123 | "reference": "5084b23845c24dbff8ac6c204290c341e4776c92" 124 | }, 125 | "dist": { 126 | "type": "zip", 127 | "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/5084b23845c24dbff8ac6c204290c341e4776c92", 128 | "reference": "5084b23845c24dbff8ac6c204290c341e4776c92", 129 | "shasum": "" 130 | }, 131 | "require": { 132 | "php": "^5.4 || ^7.0", 133 | "phpoption/phpoption": "^1.5", 134 | "symfony/polyfill-ctype": "^1.9" 135 | }, 136 | "require-dev": { 137 | "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0" 138 | }, 139 | "type": "library", 140 | "extra": { 141 | "branch-alias": { 142 | "dev-master": "3.4-dev" 143 | } 144 | }, 145 | "autoload": { 146 | "psr-4": { 147 | "Dotenv\\": "src/" 148 | } 149 | }, 150 | "notification-url": "https://packagist.org/downloads/", 151 | "license": [ 152 | "BSD-3-Clause" 153 | ], 154 | "authors": [ 155 | { 156 | "name": "Vance Lucas", 157 | "email": "vance@vancelucas.com", 158 | "homepage": "http://www.vancelucas.com" 159 | } 160 | ], 161 | "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", 162 | "keywords": [ 163 | "dotenv", 164 | "env", 165 | "environment" 166 | ], 167 | "time": "2019-06-15T22:40:20+00:00" 168 | } 169 | ], 170 | "packages-dev": [], 171 | "aliases": [], 172 | "minimum-stability": "stable", 173 | "stability-flags": [], 174 | "prefer-stable": false, 175 | "prefer-lowest": false, 176 | "platform": [], 177 | "platform-dev": [] 178 | } 179 | -------------------------------------------------------------------------------- /application/index.php: -------------------------------------------------------------------------------- 1 | |$DOCKER_URI|g" Dockerrun.aws.json 39 | # Compress deployment artifact 40 | - zip $FILE_NAME -r * .[^.]* 41 | # Copy AWS docker file to S3 42 | - aws s3 cp "$FILE_NAME" s3://$BUILD_BUCKET/$BUILD_PATH/"$FILE_NAME" 43 | # Create EB Application Version 44 | - aws elasticbeanstalk create-application-version --application-name "$APP_NAME" --version-label "$VERSION_NAME" --description "$GIT_DESCRIPTION" --source-bundle S3Bucket=$BUILD_BUCKET,S3Key="$BUILD_PATH/$FILE_NAME" 45 | deployStaging: &deployStaging 46 | name: Deploy to Staging 47 | deployment: staging 48 | trigger: manual 49 | script: 50 | # Set environment variable 51 | - export VERSION_NAME=${BITBUCKET_BUILD_NUMBER}_${BITBUCKET_TAG:=$BITBUCKET_BRANCH} 52 | # Deploy api 53 | - aws elasticbeanstalk update-environment --application-name "$APP_NAME" --environment-name "$APP_ENV_STAGING" --version-label "$VERSION_NAME" 54 | deployProduction: &deployProduction 55 | name: Deploy to Production 56 | deployment: production 57 | trigger: manual 58 | script: 59 | # Set environment variable 60 | - export VERSION_NAME=${BITBUCKET_BUILD_NUMBER}_${BITBUCKET_TAG:=$BITBUCKET_BRANCH} 61 | # Deploy api 62 | - aws elasticbeanstalk update-environment --application-name "$APP_NAME" --environment-name "$APP_ENV_PRODUCTION" --version-label "$VERSION_NAME" 63 | pipelines: 64 | branches: 65 | release-*: 66 | - step: *composerInstall 67 | - step: *buildImage 68 | - step: *sendToAws 69 | - step: *deployStaging 70 | - step: *deployProduction -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | eb_app: 4 | build: environment/containers/development/eb_single_php_container_app 5 | container_name: eb_app 6 | volumes: 7 | - ./application:/var/www/html 8 | - ./logs/app:/var/log/httpd 9 | labels: 10 | - "traefik.frontend.rule=Host:local.eb.test.com" 11 | - "traefik.port=80" 12 | extra_hosts: 13 | - "local.eb.test.com:127.0.0.1" 14 | network_mode: "bridge" 15 | -------------------------------------------------------------------------------- /environment/artifact/.ebextensions/post_deploy.config: -------------------------------------------------------------------------------- 1 | commands: 2 | create_post_dir: 3 | command: "mkdir /opt/elasticbeanstalk/hooks/appdeploy/post" 4 | ignoreErrors: true 5 | files: 6 | "/opt/elasticbeanstalk/hooks/appdeploy/post/00_generate_envvars_file.sh": 7 | mode: "000755" 8 | owner: root 9 | group: root 10 | content: | 11 | #!/usr/bin/env bash 12 | # Make support directory 13 | mkdir /opt/elasticbeanstalk/support 14 | 15 | # Export environment variables (key=value) list to file 16 | /opt/elasticbeanstalk/containerfiles/support/generate_env environment > /opt/elasticbeanstalk/support/envvars 17 | "/opt/elasticbeanstalk/hooks/appdeploy/post/01_update_motd.sh": 18 | mode: "000755" 19 | owner: root 20 | group: root 21 | content: | 22 | #!/usr/bin/env bash 23 | update-motd --disable 24 | 25 | source /opt/elasticbeanstalk/support/envvars 26 | echo "This is a zimosworld.com manager server. 27 | 28 | Application: $APP_NAME 29 | Environment: $APP_ENV 30 | 31 | Special Commands 32 | =================== 33 | You can access the root user with: 34 | sudo su" > /etc/motd 35 | "/opt/elasticbeanstalk/hooks/appdeploy/post/10_env_configs.sh": 36 | mode: "000755" 37 | owner: root 38 | group: root 39 | content: | 40 | #!/usr/bin/env bash 41 | 42 | source /opt/elasticbeanstalk/support/envvars 43 | 44 | # Get the running container ID 45 | CONTAINERID=$(docker ps -q) 46 | 47 | # Download the .env file from S3 48 | aws s3 cp s3://$BUILD_CONFIG_BUCKET/configs/$APP_NAME/$APP_ENV.env /tmp/.env 49 | 50 | # Copy the .env into the container 51 | docker cp /tmp/.env $CONTAINERID:/var/www/html 52 | 53 | # Update .env ownership in container 54 | docker exec $CONTAINERID chown -R www-data: /var/www/html/.env 55 | 56 | # Remove tmp .env file 57 | rm -f /tmp/.env -------------------------------------------------------------------------------- /environment/artifact/Dockerrun.aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSEBDockerrunVersion": "1", 3 | "Image": { 4 | "Name": "", 5 | "Update": "true" 6 | }, 7 | "Ports": [ 8 | { 9 | "ContainerPort": "80", 10 | "HostPort": "80" 11 | } 12 | ], 13 | "volumes": [], 14 | "Logging": "/var/log/apache" 15 | } -------------------------------------------------------------------------------- /environment/containers/development/eb_single_php_container_app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.3-apache 2 | MAINTAINER "Jonathan Zimros " 3 | 4 | # Add in apache config file 5 | ADD apache-config.conf /etc/httpd/conf.d/000-default.conf 6 | 7 | # Do extra stuff like install xDebug 8 | -------------------------------------------------------------------------------- /environment/containers/development/eb_single_php_container_app/apache-config.conf: -------------------------------------------------------------------------------- 1 | NameVirtualHost *:80 2 | 3 | ServerName local.eb.test.com 4 | 5 | DocumentRoot /var/www/html/ 6 | 7 | RemoteIPHeader X-Forwarded-For 8 | 9 | 10 | AllowOverride All 11 | Order allow,deny 12 | Allow from all 13 | 14 | 15 | 16 | SetEnvIf X-Forwarded-Proto ^https HTTPS 17 | 18 | 19 | LogLevel warn 20 | 21 | 22 | -------------------------------------------------------------------------------- /environment/containers/production/eb_single_php_container_app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.3-apache 2 | MAINTAINER "Jonathan Zimros " 3 | 4 | # Add Apache config 5 | ADD ./environment/containers/production/web-portal-api/apache-config.conf /etc/apache2/sites-available/000-default.conf 6 | 7 | # Copy project files 8 | COPY ./application /var/www/html/ 9 | # Update ownership of project files 10 | RUN chown -R www-data: /var/www/html -------------------------------------------------------------------------------- /environment/containers/production/eb_single_php_container_app/apache-config.conf: -------------------------------------------------------------------------------- 1 | 2 | DocumentRoot /var/www/html/public 3 | 4 | 5 | AllowOverride All 6 | Order allow,deny 7 | Allow from all 8 | 9 | 10 | LogLevel warn 11 | 12 | 13 | -------------------------------------------------------------------------------- /environment/infrastructure/application/README.md: -------------------------------------------------------------------------------- 1 | # EB Single Container App Example Global Infrastructure 2 | * Written using terraform 3 | 4 | ## Prerequisites 5 | 6 | * Your AWS CLI credentials stored in your user profile 7 | * Your Terraform Token stored in your user profile 8 | 9 | See [here](https://www.terraform.io/docs/enterprise/free/index.html) for details on Terraform Enterprise and [here](https://app.terraform.io/signup) for setting up a free account. 10 | 11 | ## How to Use 12 | 13 | To apply changes you will need to do the following: 14 | 15 | * Change to this directory (environment/infrastructure/application) 16 | * Initialize terraform 17 | * Select the workspace 18 | * Run apply 19 | 20 | ### Commands 21 | 22 | #### Terraform Initialize 23 | 24 | ```bash 25 | terraform init 26 | ``` 27 | 28 | #### Terraform workspace 29 | 30 | ```bash 31 | # Show the current workspace 32 | terraform workspace show 33 | 34 | # List all available workspaces 35 | terraform workspace list 36 | 37 | # Change current workspace 38 | # [$workspace] is a place holder for the workspace name 39 | terraform workspace select [$workspace] 40 | ``` 41 | 42 | #### Terraform apply 43 | 44 | ##### If using [Eiger](https://github.com/zimosworld/eiger) to run terraform 45 | 46 | ```bash 47 | terraform apply 48 | ``` 49 | 50 | ##### If using a locally installed version of terraform 51 | 52 | [$env] is a place holder for the environment name 53 | 54 | ```bash 55 | terraform apply -var-file=config/[$env].tfvars 56 | ``` -------------------------------------------------------------------------------- /environment/infrastructure/application/backend.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Backend 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | terraform { 8 | backend "remote" { 9 | // Values in backend can not be variables 10 | organization = "container-testing" 11 | 12 | workspaces { 13 | prefix = "eb-single-container-app-" 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /environment/infrastructure/application/beanstalk_environment.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Beanstalk Environment 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | module "elastic_beanstalk_environment" { 8 | source = "git::https://github.com/zimosworld/terraform-aws-beanstalk-clb.git?ref=0.0.5" 9 | 10 | name = var.aws_beanstalk_environment["name"] 11 | description = var.aws_beanstalk_environment["description"] 12 | 13 | application = var.aws_beanstalk_environment["application_name"] 14 | vpc_id = data.aws_vpc.default.id 15 | private_subnets = data.aws_subnet_ids.private_subnets.ids 16 | public_subnets = data.aws_subnet_ids.public_subnets.ids 17 | keypair = var.aws_beanstalk_environment["ssh_key_name"] 18 | env_vars = var.aws_beanstalk_environment["env_vars"] 19 | 20 | solution_stack = var.aws_beanstalk_environment["solution_stack"] 21 | logs_retention_in_days = var.aws_beanstalk_environment["logs_retention_in_days"] 22 | instance_type = var.aws_beanstalk_environment["instance_type"] 23 | ssh_source_restriction = var.aws_beanstalk_environment["ssh_source_restriction"] 24 | clb_ssl_listener_enabled = var.aws_beanstalk_environment["enable_ssl"] 25 | #clb_ssl_listener_certificate_arn = "${aws_acm_certificate.default.arn}" 26 | healthcheck_url = var.aws_beanstalk_environment["healthcheck_url"] 27 | 28 | tags = merge({ 29 | terraform = "true" 30 | application = var.tag_application_name 31 | environment = var.tag_application_environment 32 | }, var.aws_beanstalk_environment["tags"]) 33 | } 34 | 35 | #-------------------------------------------------------------- 36 | # Variables 37 | #-------------------------------------------------------------- 38 | 39 | variable "aws_beanstalk_environment" { 40 | description = "Map of Elastic Beanstalk configs" 41 | type = object({ 42 | name = string 43 | description = string 44 | application_name = string 45 | ssh_key_name = string 46 | tags = map(string) 47 | env_vars = map(string) 48 | solution_stack = string 49 | logs_retention_in_days = string 50 | instance_type = string 51 | ssh_source_restriction = string 52 | enable_ssl = bool 53 | healthcheck_url = string 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /environment/infrastructure/application/config/production.tfvars: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # General 3 | #-------------------------------------------------------------- 4 | 5 | aws_region = "ap-southeast-2" 6 | 7 | tag_application_name = "eb-single-container-app" 8 | tag_application_environment = "production" 9 | 10 | #-------------------------------------------------------------- 11 | # Network 12 | #-------------------------------------------------------------- 13 | 14 | aws_vpc_identifier = "2fH8nZ" 15 | 16 | #-------------------------------------------------------------- 17 | # Database 18 | #-------------------------------------------------------------- 19 | 20 | aws_rds = [ 21 | { 22 | storage_type = "gp2" 23 | allocated_storage = "20" 24 | engine = "mariadb" 25 | engine_version = "10.3" 26 | instance = "db.t3.micro" 27 | identifier = "eb-single-container-db-production" 28 | db_name = "eb-db" 29 | username = "master" 30 | parameter_group_name = "default.mariadb10.3" 31 | db_subnet_group_name = "sct-production" 32 | tags = {} 33 | } 34 | ] 35 | 36 | #-------------------------------------------------------------- 37 | # Beanstalk application 38 | #-------------------------------------------------------------- 39 | 40 | aws_beanstalk_environment = { 41 | name = "eb-single-container-app-production" 42 | description = "EB Single Container App Production" 43 | application_name = "EB Single Container App" 44 | ssh_key_name = "sct-key" 45 | tags = { 46 | APP_ENV: "production" 47 | APP_NAME: "eb-single-container-app" 48 | BUILD_CONFIG_BUCKET: "" 49 | } 50 | solution_stack = "running Docker" 51 | logs_retention_in_days = "30" 52 | instance_type = "t3.micro" 53 | ssh_source_restriction = "172.31.0.0/20" 54 | enable_ssl = false 55 | healthcheck_url = "/health.php" 56 | } -------------------------------------------------------------------------------- /environment/infrastructure/application/config/staging.tfvars: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # General 3 | #-------------------------------------------------------------- 4 | 5 | aws_region = "ap-southeast-2" 6 | 7 | tag_application_name = "eb-single-container-app" 8 | tag_application_environment = "staging" 9 | 10 | #-------------------------------------------------------------- 11 | # Network 12 | #-------------------------------------------------------------- 13 | 14 | aws_vpc_identifier = "scSJ8p" 15 | 16 | #-------------------------------------------------------------- 17 | # Database 18 | #-------------------------------------------------------------- 19 | 20 | aws_rds = [ 21 | { 22 | storage_type = "gp2" 23 | allocated_storage = "20" 24 | engine = "mariadb" 25 | engine_version = "10.3" 26 | instance = "db.t3.micro" 27 | identifier = "eb-single-container-db-staging" 28 | db_name = "eb-db" 29 | username = "master" 30 | parameter_group_name = "default.mariadb10.3" 31 | db_subnet_group_name = "sct-staging" 32 | tags = {} 33 | } 34 | ] 35 | 36 | #-------------------------------------------------------------- 37 | # Beanstalk application 38 | #-------------------------------------------------------------- 39 | 40 | aws_beanstalk_environment = { 41 | name = "eb-single-container-app-staging" 42 | description = "EB Single Container App staging" 43 | application_name = "EB Single Container App" 44 | ssh_key_name = "sct-key" 45 | tags = { 46 | APP_ENV: "staging" 47 | APP_NAME: "eb-single-container-app" 48 | BUILD_CONFIG_BUCKET: "" 49 | } 50 | solution_stack = "running Docker" 51 | logs_retention_in_days = "30" 52 | instance_type = "t3.micro" 53 | ssh_source_restriction = "172.31.0.0/20" 54 | enable_ssl = false 55 | healthcheck_url = "/health.php" 56 | } -------------------------------------------------------------------------------- /environment/infrastructure/application/database.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Database 4 | # 5 | #-------------------------------------------------------------- 6 | resource "random_string" "database_password" { 7 | length = 21 8 | special = false 9 | } 10 | 11 | resource "aws_db_instance" "default" { 12 | count = length(var.aws_rds) 13 | 14 | allocated_storage = var.aws_rds[count.index]["allocated_storage"] 15 | storage_type = var.aws_rds[count.index]["storage_type"] 16 | engine = var.aws_rds[count.index]["engine"] 17 | engine_version = var.aws_rds[count.index]["engine_version"] 18 | instance_class = var.aws_rds[count.index]["instance"] 19 | identifier = var.aws_rds[count.index]["identifier"] 20 | name = var.aws_rds[count.index]["db_name"] 21 | username = var.aws_rds[count.index]["username"] 22 | password = random_string.database_password.result 23 | parameter_group_name = var.aws_rds[count.index]["parameter_group_name"] 24 | db_subnet_group_name = var.aws_rds[count.index]["db_subnet_group_name"] 25 | 26 | tags = merge({ 27 | terraform = "true" 28 | application = var.tag_application_name 29 | environment = var.tag_application_environment 30 | }, var.aws_rds[count.index]["tags"]) 31 | 32 | lifecycle { 33 | ignore_changes = [ 34 | password] 35 | } 36 | } 37 | 38 | #-------------------------------------------------------------- 39 | # Variables 40 | #-------------------------------------------------------------- 41 | 42 | variable "aws_rds" { 43 | description = "List of RDS configs" 44 | type = list(object({ 45 | storage_type = string 46 | allocated_storage = string 47 | engine = string 48 | engine_version = string 49 | instance = string 50 | identifier = string 51 | db_name = string 52 | username = string 53 | parameter_group_name = string 54 | db_subnet_group_name = string 55 | tags = map(string) 56 | })) 57 | } 58 | -------------------------------------------------------------------------------- /environment/infrastructure/application/network.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Network 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | data "aws_vpc" "default" { 8 | tags = { 9 | environment = var.tag_application_environment 10 | identifier = var.aws_vpc_identifier 11 | } 12 | } 13 | 14 | data "aws_subnet_ids" "public_subnets" { 15 | vpc_id = data.aws_vpc.default.id 16 | 17 | tags = { 18 | subnet_type = "public" 19 | } 20 | } 21 | 22 | data "aws_subnet_ids" "private_subnets" { 23 | vpc_id = data.aws_vpc.default.id 24 | 25 | tags = { 26 | subnet_type = "private" 27 | } 28 | } 29 | 30 | data "aws_subnet_ids" "database_subnets" { 31 | vpc_id = data.aws_vpc.default.id 32 | 33 | tags = { 34 | subnet_type = "database" 35 | } 36 | } 37 | 38 | #-------------------------------------------------------------- 39 | # Variables 40 | #-------------------------------------------------------------- 41 | 42 | variable "aws_vpc_identifier" { 43 | description = "Unique identifier tag on vpc" 44 | } 45 | 46 | -------------------------------------------------------------------------------- /environment/infrastructure/application/provider.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Provider 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | provider "aws" { 8 | region = var.aws_region 9 | } 10 | 11 | #-------------------------------------------------------------- 12 | # Variables 13 | #-------------------------------------------------------------- 14 | 15 | variable "aws_region" { 16 | description = "Region for the VPC" 17 | } 18 | 19 | variable "aws_role" { 20 | description = "Role Switch ARN" 21 | } 22 | 23 | variable "tag_application_name" { 24 | description = "Application name tag" 25 | } 26 | 27 | variable "tag_application_environment" { 28 | description = "Application environment tag" 29 | } 30 | 31 | -------------------------------------------------------------------------------- /environment/infrastructure/application/versions.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Terraform version required 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | terraform { 8 | required_version = ">= 0.12" 9 | } 10 | -------------------------------------------------------------------------------- /environment/infrastructure/global/README.md: -------------------------------------------------------------------------------- 1 | # EB Single Container App Example Global Infrastructure 2 | * Written using terraform 3 | 4 | ## Prerequisites 5 | 6 | * Your AWS CLI credentials stored in your user profile 7 | * Your Terraform Enterprise Token* stored in your user profile 8 | 9 | See [here](https://www.terraform.io/docs/enterprise/free/index.html) for details on Terraform Enterprise and [here](https://app.terraform.io/signup) for setting up a free account. 10 | 11 | ## How to Use 12 | 13 | To apply changes you will need to do the following: 14 | 15 | * Change to this directory (environment/infrastructure/global) 16 | * Initialize terraform 17 | * Run apply 18 | 19 | ### Commands 20 | 21 | #### Terraform Initialize 22 | 23 | ```bash 24 | terraform init 25 | ``` 26 | 27 | #### Terraform apply, plan or destroy 28 | 29 | [$command] is a place holder for the command to run 30 | 31 | ```bash 32 | terraform [$command] 33 | 34 | i.e 35 | terraform apply 36 | ``` 37 | -------------------------------------------------------------------------------- /environment/infrastructure/global/backend.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Backend 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | terraform { 8 | backend "remote" { 9 | organization = "container-testing" 10 | 11 | workspaces { 12 | name = "eb-single-container-app-global" 13 | } 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /environment/infrastructure/global/beanstalk_applications.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Beanstalk Applications 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | resource "aws_elastic_beanstalk_application" "default" { 8 | count = length(var.aws_beanstalk_apps) 9 | 10 | name = var.aws_beanstalk_apps[count.index]["name"] 11 | description = var.aws_beanstalk_apps[count.index]["description"] 12 | } 13 | 14 | #-------------------------------------------------------------- 15 | # Variables 16 | #-------------------------------------------------------------- 17 | 18 | variable "aws_beanstalk_apps" { 19 | description = "List of Beanstalk Applications for create" 20 | type = list(object({ 21 | name: string 22 | description: string 23 | })) 24 | } -------------------------------------------------------------------------------- /environment/infrastructure/global/container_registry.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Container Registry 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | resource "aws_ecr_repository" "default" { 8 | count = length(var.aws_ecr) 9 | name = var.aws_ecr[count.index]["name"] 10 | 11 | tags = merge({ 12 | terraform: true 13 | }, var.aws_ecr[count.index]["tags"]) 14 | } 15 | 16 | #-------------------------------------------------------------- 17 | # Variables 18 | #-------------------------------------------------------------- 19 | 20 | variable "aws_ecr" { 21 | description = "List of ECR to create" 22 | type = list(object({ 23 | name = string 24 | tags = map(string) 25 | })) 26 | } 27 | -------------------------------------------------------------------------------- /environment/infrastructure/global/provider.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Provider 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | provider "aws" { 8 | region = var.aws_region 9 | version = "~> 2.7" 10 | } 11 | 12 | #-------------------------------------------------------------- 13 | # Variables 14 | #-------------------------------------------------------------- 15 | 16 | variable "aws_region" { 17 | description = "Region for the VPC" 18 | } 19 | 20 | variable "aws_role" { 21 | description = "Role Switch ARN" 22 | } 23 | 24 | -------------------------------------------------------------------------------- /environment/infrastructure/global/s3_bucket.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # S3 Bucket 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | resource "aws_s3_bucket" "default" { 8 | count = length(var.aws_s3_buckets) 9 | 10 | bucket_prefix = var.aws_s3_buckets[count.index]["name_prefix"] 11 | acl = var.aws_s3_buckets[count.index]["acl"] 12 | 13 | tags = merge({ 14 | terraform = "true" 15 | environment = "global" 16 | }, var.aws_s3_buckets[count.index]["tags"]) 17 | } 18 | 19 | #-------------------------------------------------------------- 20 | # Variables 21 | #-------------------------------------------------------------- 22 | 23 | variable "aws_s3_buckets" { 24 | description = "List of objects for buckets with settings to create" 25 | type = list(object({ 26 | name_prefix = string 27 | acl = string 28 | tags = map(string) 29 | })) 30 | } -------------------------------------------------------------------------------- /environment/infrastructure/global/ssh_key.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # AWS SSH Key 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | resource "aws_key_pair" "ssh-key" { 8 | key_name = var.aws_ssh_key_name 9 | public_key = var.aws_ssh_key 10 | } 11 | 12 | #-------------------------------------------------------------- 13 | # Variables 14 | #-------------------------------------------------------------- 15 | 16 | variable "aws_ssh_key_name" { 17 | description = "SSH Key Name" 18 | } 19 | 20 | variable "aws_ssh_key" { 21 | description = "SSH Key" 22 | } 23 | 24 | -------------------------------------------------------------------------------- /environment/infrastructure/global/terraform.tfvars: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Global Terraform Varaiables 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | #-------------------------------------------------------------- 8 | # General 9 | #-------------------------------------------------------------- 10 | 11 | aws_region = "ap-southeast-2" 12 | 13 | #-------------------------------------------------------------- 14 | # VPC 15 | #-------------------------------------------------------------- 16 | 17 | aws_vpc_production = { 18 | name = "sct-production" 19 | cidr = "10.0.16.0/20" 20 | availability_zones = ["ap-southeast-2a", "ap-southeast-2b", "ap-southeast-2c"] 21 | public_subnets = ["10.0.16.0/24", "10.0.19.0/24", "10.0.22.0/24"] 22 | private_subnets = ["10.0.17.0/24", "10.0.20.0/24", "10.0.23.0/24"] 23 | database_subnets = ["10.0.18.0/24", "10.0.21.0/24", "10.0.24.0/24"] 24 | enable_nat_gateway = false 25 | single_nat_gateway = true 26 | public_subnet_tags = { subnet_type = "public" } 27 | private_subnet_tags = { subnet_type = "private" } 28 | database_subnet_tags = { subnet_type = "database" } 29 | tags = {} 30 | vpc_tags = { 31 | environment = "production" 32 | identifier = "2fH8nZ" 33 | } 34 | } 35 | 36 | aws_vpc_staging = { 37 | name = "sct-staging" 38 | cidr = "10.0.32.0/20" 39 | availability_zones = ["ap-southeast-2a", "ap-southeast-2b", "ap-southeast-2c"] 40 | public_subnets = ["10.0.32.0/24", "10.0.35.0/24", "10.0.38.0/24"] 41 | private_subnets = ["10.0.33.0/24", "10.0.36.0/24", "10.0.39.0/24"] 42 | database_subnets = ["10.0.34.0/24", "10.0.37.0/24", "10.0.40.0/24"] 43 | enable_nat_gateway = false 44 | single_nat_gateway = true 45 | public_subnet_tags = { subnet_type = "public" } 46 | private_subnet_tags = { subnet_type = "private" } 47 | database_subnet_tags = { subnet_type = "database" } 48 | tags = {} 49 | vpc_tags = { 50 | environment = "staging" 51 | identifier = "scSJ8p" 52 | } 53 | } 54 | 55 | 56 | #-------------------------------------------------------------- 57 | # Beanstalk Apps 58 | #-------------------------------------------------------------- 59 | 60 | aws_beanstalk_apps = [ 61 | { 62 | name = "EB Single Container App" 63 | description = "EB Single Container App Example" 64 | } 65 | ] 66 | 67 | #-------------------------------------------------------------- 68 | # S3 Buckets 69 | #-------------------------------------------------------------- 70 | 71 | aws_s3_buckets = [ 72 | { 73 | name_prefix = "ct-builds-configs" 74 | acl = "private" 75 | tags = {} 76 | } 77 | ] 78 | 79 | #-------------------------------------------------------------- 80 | # SSH Key 81 | #-------------------------------------------------------------- 82 | 83 | aws_ssh_key_name = "sct-key" 84 | aws_ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+AJcQTPZd1ar5CXynmpDteezoNIEWtWByWu4SBz+me67sr7QEPp8SGPjT4zJNgtLSgIkr+4d1UUTEhiUEyURwkk+959lt8DEUSPAmYRsLZ7WdvA1Ov35lojlDY0vNQsNtiEesfV9t1+0RwcHkgzFDPDEj+KzA8MebuZYcynvVQ8N/3d0tpvO8VtWLC5zWXE2y528lW4gfMDvlJihY4kT4z2igEvXYL0cfJf1+GJ48fRPfF4rXow91tzeqglqgJbhW/gmnPjCy8lIYkB1QANZHzyNCpV/e/xUt3bQAn4on1GxV5ySTFeypu05WOiT2uvEEI6azge3wSDJdjLWqp25R" 85 | 86 | -------------------------------------------------------------------------------- /environment/infrastructure/global/versions.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # Terraform version required 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | terraform { 8 | required_version = ">= 0.12" 9 | } 10 | -------------------------------------------------------------------------------- /environment/infrastructure/global/vpc-production.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # AWS VPC 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | module "vpc-production" { 8 | source = "terraform-aws-modules/vpc/aws" 9 | 10 | name = var.aws_vpc_production["name"] 11 | cidr = var.aws_vpc_production["cidr"] 12 | 13 | azs = var.aws_vpc_production["availability_zones"] 14 | public_subnets = var.aws_vpc_production["public_subnets"] 15 | private_subnets = var.aws_vpc_production["private_subnets"] 16 | database_subnets = var.aws_vpc_production["database_subnets"] 17 | 18 | enable_nat_gateway = var.aws_vpc_production["enable_nat_gateway"] 19 | single_nat_gateway = var.aws_vpc_production["single_nat_gateway"] 20 | 21 | public_subnet_tags = var.aws_vpc_production["public_subnet_tags"] 22 | private_subnet_tags = var.aws_vpc_production["private_subnet_tags"] 23 | database_subnet_tags = var.aws_vpc_production["database_subnet_tags"] 24 | 25 | tags = merge({ 26 | terraform = "true" 27 | }, var.aws_vpc_production["tags"]) 28 | 29 | vpc_tags = var.aws_vpc_production["vpc_tags"] 30 | } 31 | 32 | #-------------------------------------------------------------- 33 | # Variables 34 | #-------------------------------------------------------------- 35 | 36 | variable "aws_vpc_production" { 37 | description = "Object with VPC settings" 38 | type = object({ 39 | name = string 40 | cidr = string 41 | availability_zones = list(string) 42 | public_subnets = list(string) 43 | private_subnets = list(string) 44 | database_subnets = list(string) 45 | enable_nat_gateway = bool 46 | single_nat_gateway = bool 47 | public_subnet_tags = map(string) 48 | private_subnet_tags = map(string) 49 | database_subnet_tags = map(string) 50 | tags = map(string) 51 | vpc_tags = map(string) 52 | }) 53 | } -------------------------------------------------------------------------------- /environment/infrastructure/global/vpc-staging.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # 3 | # AWS VPC 4 | # 5 | #-------------------------------------------------------------- 6 | 7 | module "vpc-staging" { 8 | source = "terraform-aws-modules/vpc/aws" 9 | 10 | name = var.aws_vpc_staging["name"] 11 | cidr = var.aws_vpc_staging["cidr"] 12 | 13 | azs = var.aws_vpc_staging["availability_zones"] 14 | public_subnets = var.aws_vpc_staging["public_subnets"] 15 | private_subnets = var.aws_vpc_staging["private_subnets"] 16 | database_subnets = var.aws_vpc_staging["database_subnets"] 17 | 18 | enable_nat_gateway = var.aws_vpc_staging["enable_nat_gateway"] 19 | single_nat_gateway = var.aws_vpc_staging["single_nat_gateway"] 20 | 21 | public_subnet_tags = var.aws_vpc_staging["public_subnet_tags"] 22 | private_subnet_tags = var.aws_vpc_staging["private_subnet_tags"] 23 | database_subnet_tags = var.aws_vpc_staging["database_subnet_tags"] 24 | 25 | tags = merge({ 26 | terraform = "true" 27 | }, var.aws_vpc_staging["tags"]) 28 | 29 | vpc_tags = var.aws_vpc_staging["vpc_tags"] 30 | } 31 | 32 | #-------------------------------------------------------------- 33 | # Variables 34 | #-------------------------------------------------------------- 35 | 36 | variable "aws_vpc_staging" { 37 | description = "Object with VPC settings" 38 | type = object({ 39 | name = string 40 | cidr = string 41 | availability_zones = list(string) 42 | public_subnets = list(string) 43 | private_subnets = list(string) 44 | database_subnets = list(string) 45 | enable_nat_gateway = bool 46 | single_nat_gateway = bool 47 | public_subnet_tags = map(string) 48 | private_subnet_tags = map(string) 49 | database_subnet_tags = map(string) 50 | tags = map(string) 51 | vpc_tags = map(string) 52 | }) 53 | } --------------------------------------------------------------------------------