├── .gitignore ├── examples ├── gitlab-repository-webhook │ ├── terraform.tfvars.sample │ ├── variables.tf │ ├── outputs.tf │ ├── main.tf │ └── README.md └── github-repository-webhook │ ├── terraform.tfvars.sample │ ├── variables.tf │ ├── outputs.tf │ ├── main.tf │ └── README.md ├── .pre-commit-config.yaml ├── modules ├── gitlab-repository-webhook │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md └── github-repository-webhook │ ├── outputs.tf │ ├── variables.tf │ ├── main.tf │ └── README.md ├── LICENSE ├── .editorconfig ├── outputs.tf ├── terraform.tfvars.sample ├── variables.tf ├── README.md └── main.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | *.tfstate* 3 | terraform.tfvars 4 | 5 | tls/ 6 | generate-tls/ 7 | terraform/ 8 | -------------------------------------------------------------------------------- /examples/gitlab-repository-webhook/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | gitlab_token = "mygitlabtoken" 2 | gitlab_base_url = "" 3 | -------------------------------------------------------------------------------- /examples/github-repository-webhook/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | github_token = "mygithubtoken" 2 | github_organization = "myusername" 3 | -------------------------------------------------------------------------------- /examples/github-repository-webhook/variables.tf: -------------------------------------------------------------------------------- 1 | variable "github_token" { 2 | description = "Github token" 3 | } 4 | 5 | variable "github_organization" { 6 | description = "Github organization" 7 | } 8 | -------------------------------------------------------------------------------- /examples/gitlab-repository-webhook/variables.tf: -------------------------------------------------------------------------------- 1 | variable "gitlab_token" { 2 | description = "Gitlab token" 3 | } 4 | 5 | variable "gitlab_base_url" { 6 | description = "Gitlab base_url" 7 | default = "" 8 | } 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.8.1 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_docs 7 | - repo: git://github.com/pre-commit/pre-commit-hooks 8 | rev: v2.1.0 9 | hooks: 10 | - id: check-merge-conflict 11 | -------------------------------------------------------------------------------- /modules/gitlab-repository-webhook/outputs.tf: -------------------------------------------------------------------------------- 1 | output "this_repository_webhook_urls" { 2 | description = "Webhook URL" 3 | value = ["${gitlab_project_hook.this.*.url}"] 4 | } 5 | 6 | output "this_repository_webhook_secret" { 7 | description = "Webhook secret" 8 | value = "${var.webhook_secret}" 9 | } 10 | -------------------------------------------------------------------------------- /modules/github-repository-webhook/outputs.tf: -------------------------------------------------------------------------------- 1 | output "this_repository_webhook_urls" { 2 | description = "Webhook URL" 3 | value = ["${github_repository_webhook.this.*.url}"] 4 | } 5 | 6 | output "this_repository_webhook_secret" { 7 | description = "Webhook secret" 8 | value = "${var.webhook_secret}" 9 | } 10 | -------------------------------------------------------------------------------- /examples/github-repository-webhook/outputs.tf: -------------------------------------------------------------------------------- 1 | output "github_webhook_urls" { 2 | description = "Github webhook URL" 3 | value = "${module.github_repository_webhook.this_repository_webhook_urls}" 4 | } 5 | 6 | output "github_webhook_secret" { 7 | description = "Github webhook secret" 8 | value = "${module.github_repository_webhook.this_repository_webhook_secret}" 9 | } 10 | -------------------------------------------------------------------------------- /examples/gitlab-repository-webhook/outputs.tf: -------------------------------------------------------------------------------- 1 | output "gitlab_webhook_urls" { 2 | description = "Gitlab webhook URL" 3 | value = "${module.gitlab_repository_webhook.this_repository_webhook_urls}" 4 | } 5 | 6 | output "gitlab_webhook_secret" { 7 | description = "Gitlab webhook secret" 8 | value = "${module.gitlab_repository_webhook.this_repository_webhook_secret}" 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 -------------------------------------------------------------------------------- /modules/gitlab-repository-webhook/main.tf: -------------------------------------------------------------------------------- 1 | provider "gitlab" { 2 | token = "${var.gitlab_token}" 3 | base_url = "${var.gitlab_base_url}" 4 | } 5 | 6 | resource "gitlab_project_hook" "this" { 7 | count = "${var.create_gitlab_repository_webhook && length(var.atlantis_allowed_repo_names) > 0 ? length(var.atlantis_allowed_repo_names) : 0}" 8 | 9 | project = "${var.atlantis_allowed_repo_names[count.index]}" 10 | url = "${var.webhook_url}" 11 | token = "${var.webhook_secret}" 12 | enable_ssl_verification = false 13 | 14 | merge_requests_events = true 15 | push_events = true 16 | note_events = true 17 | } 18 | -------------------------------------------------------------------------------- /examples/gitlab-repository-webhook/main.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "atlantis" { 2 | backend = "local" 3 | 4 | config { 5 | path = "../../terraform.tfstate" 6 | } 7 | } 8 | 9 | module "gitlab_repository_webhook" { 10 | source = "../../modules/gitlab-repository-webhook" 11 | 12 | create_gitlab_repository_webhook = true 13 | 14 | gitlab_token = "${var.gitlab_token}" 15 | gitlab_base_url = "${var.gitlab_base_url}" 16 | 17 | // Fetching these attributes from created already Atlantis Terraform state file 18 | atlantis_allowed_repo_names = "${data.terraform_remote_state.atlantis.atlantis_allowed_repo_names}" 19 | webhook_url = "${data.terraform_remote_state.atlantis.atlantis_url_events}" 20 | webhook_secret = "${data.terraform_remote_state.atlantis.webhook_secret}" 21 | } 22 | -------------------------------------------------------------------------------- /modules/github-repository-webhook/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create_github_repository_webhook" { 2 | description = "Whether to create Github repository webhook for Atlantis" 3 | default = true 4 | } 5 | 6 | variable "github_token" { 7 | description = "Github token to use when creating webhook" 8 | default = "" 9 | } 10 | 11 | variable "github_organization" { 12 | description = "Github organization to use when creating webhook" 13 | default = "" 14 | } 15 | 16 | variable "atlantis_allowed_repo_names" { 17 | description = "List of names of repositories which belong to the organization specified in `github_organization`" 18 | type = "list" 19 | } 20 | 21 | variable "webhook_url" { 22 | description = "Webhook URL" 23 | default = "" 24 | } 25 | 26 | variable "webhook_secret" { 27 | description = "Webhook secret" 28 | default = "" 29 | } 30 | -------------------------------------------------------------------------------- /modules/github-repository-webhook/main.tf: -------------------------------------------------------------------------------- 1 | provider "github" { 2 | token = "${var.github_token}" 3 | organization = "${var.github_organization}" 4 | } 5 | 6 | resource "github_repository_webhook" "this" { 7 | count = "${var.create_github_repository_webhook && length(var.atlantis_allowed_repo_names) > 0 ? length(var.atlantis_allowed_repo_names) : 0}" 8 | 9 | name = "web" 10 | repository = "${var.atlantis_allowed_repo_names[count.index]}" 11 | 12 | configuration { 13 | url = "${var.webhook_url}" 14 | content_type = "application/json" 15 | insecure_ssl = false 16 | secret = "${var.webhook_secret}" 17 | } 18 | 19 | events = [ 20 | "issue_comment", 21 | "pull_request", 22 | "pull_request_review", 23 | "pull_request_review_comment", 24 | ] 25 | 26 | lifecycle { 27 | # The secret is saved as ******* in the state 28 | ignore_changes = ["configuration.secret"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/gitlab-repository-webhook/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create_gitlab_repository_webhook" { 2 | description = "Whether to create Gitlab repository webhook for Atlantis" 3 | default = true 4 | } 5 | 6 | variable "gitlab_base_url" { 7 | description = "Gitlab base_url use" 8 | default = "" 9 | } 10 | 11 | variable "gitlab_token" { 12 | description = "Gitlab token to use when creating webhook" 13 | default = "" 14 | } 15 | 16 | variable "gitlab_organization" { 17 | description = "Gitlab organization to use when creating webhook" 18 | default = "" 19 | } 20 | 21 | variable "atlantis_allowed_repo_names" { 22 | description = "List of names of repositories which belong to the organization specified in `gitlab_organization`" 23 | type = "list" 24 | } 25 | 26 | variable "webhook_url" { 27 | description = "Webhook URL" 28 | default = "" 29 | } 30 | 31 | variable "webhook_secret" { 32 | description = "Webhook secret" 33 | default = "" 34 | } 35 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "atlantis_url" { 2 | description = "URL of Atlantis" 3 | value = "${local.atlantis_url}" 4 | } 5 | 6 | output "atlantis_url_events" { 7 | description = "Webhook events URL of Atlantis" 8 | value = "${local.atlantis_url_events}" 9 | } 10 | 11 | output "atlantis_allowed_repo_names" { 12 | description = "Github repositories where webhook should be created" 13 | value = "${var.atlantis_allowed_repo_names}" 14 | } 15 | 16 | output "task_role_arn" { 17 | description = "The Atlantis ECS task role arn" 18 | value = "${aws_iam_role.ecs_task_execution.arn}" 19 | } 20 | 21 | output "vpc_id" { 22 | description = "ID of the VPC that was created or passed in" 23 | value = "${local.vpc_id}" 24 | } 25 | 26 | output "webhook_secret" { 27 | description = "Webhook secret" 28 | value = "${random_id.webhook.*.hex}" 29 | } 30 | 31 | output "alb_dns_name" { 32 | description = "Dns name of alb" 33 | value = "${module.alb.dns_name}" 34 | } 35 | -------------------------------------------------------------------------------- /examples/github-repository-webhook/main.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "atlantis" { 2 | backend = "local" 3 | 4 | config { 5 | path = "../../terraform.tfstate" 6 | } 7 | } 8 | 9 | module "github_repository_webhook" { 10 | source = "../../modules/github-repository-webhook" 11 | 12 | create_github_repository_webhook = true 13 | 14 | github_token = "${var.github_token}" 15 | github_organization = "${var.github_organization}" 16 | 17 | // Fetching these attributes from created already Atlantis Terraform state file 18 | // 19 | // This assumes that you are the owner of these repositories and they are available at: 20 | // https://github.com/mygithubusername/awesome-repo and https://github.com/mygithubusername/another-awesome-repo 21 | atlantis_allowed_repo_names = "${data.terraform_remote_state.atlantis.atlantis_allowed_repo_names}" 22 | 23 | webhook_url = "${data.terraform_remote_state.atlantis.atlantis_url_events}" 24 | webhook_secret = "${data.terraform_remote_state.atlantis.webhook_secret}" 25 | } 26 | -------------------------------------------------------------------------------- /modules/github-repository-webhook/README.md: -------------------------------------------------------------------------------- 1 | # Github repository webhook for Atlantis 2 | 3 | 4 | ## Inputs 5 | 6 | | Name | Description | Type | Default | Required | 7 | |------|-------------|:----:|:-----:|:-----:| 8 | | atlantis\_allowed\_repo\_names | List of names of repositories which belong to the organization specified in `github_organization` | list | n/a | yes | 9 | | create\_github\_repository\_webhook | Whether to create Github repository webhook for Atlantis | string | `"true"` | no | 10 | | github\_organization | Github organization to use when creating webhook | string | `""` | no | 11 | | github\_token | Github token to use when creating webhook | string | `""` | no | 12 | | webhook\_secret | Webhook secret | string | `""` | no | 13 | | webhook\_url | Webhook URL | string | `""` | no | 14 | 15 | ## Outputs 16 | 17 | | Name | Description | 18 | |------|-------------| 19 | | this\_repository\_webhook\_secret | Webhook secret | 20 | | this\_repository\_webhook\_urls | Webhook URL | 21 | 22 | 23 | -------------------------------------------------------------------------------- /modules/gitlab-repository-webhook/README.md: -------------------------------------------------------------------------------- 1 | # Gitlab repository webhook for Atlantis 2 | 3 | 4 | ## Inputs 5 | 6 | | Name | Description | Type | Default | Required | 7 | |------|-------------|:----:|:-----:|:-----:| 8 | | atlantis\_allowed\_repo\_names | List of names of repositories which belong to the organization specified in `gitlab_organization` | list | n/a | yes | 9 | | create\_gitlab\_repository\_webhook | Whether to create Gitlab repository webhook for Atlantis | string | `"true"` | no | 10 | | gitlab\_base\_url | Gitlab base_url use | string | `""` | no | 11 | | gitlab\_organization | Gitlab organization to use when creating webhook | string | `""` | no | 12 | | gitlab\_token | Gitlab token to use when creating webhook | string | `""` | no | 13 | | webhook\_secret | Webhook secret | string | `""` | no | 14 | | webhook\_url | Webhook URL | string | `""` | no | 15 | 16 | ## Outputs 17 | 18 | | Name | Description | 19 | |------|-------------| 20 | | this\_repository\_webhook\_secret | Webhook secret | 21 | | this\_repository\_webhook\_urls | Webhook URL | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/gitlab-repository-webhook/README.md: -------------------------------------------------------------------------------- 1 | # Gitlab repository webhook for Atlantis 2 | 3 | Configuration in this directory creates Gitlab repository webhooks configured to Atlantis URL. This example uses value of webhook secret which got generated when Atlantis setup by referring to `terraform.tfstate`, so this example has to run after Atlantis. 4 | 5 | ## Usage 6 | 7 | To run this code you need to copy `terraform.tfvars.sample` into `terraform.tfvars` and put your GitHub token and Github organization there or specify them using environment variables (`TF_VAR_gitlab_token` and `TF_VAR_gitlab_base_url`). Once ready, execute: 8 | 9 | ```bash 10 | $ terraform init 11 | $ terraform plan 12 | $ terraform apply 13 | ``` 14 | 15 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 16 | 17 | 18 | ## Inputs 19 | 20 | | Name | Description | Type | Default | Required | 21 | |------|-------------|:----:|:-----:|:-----:| 22 | | gitlab\_base\_url | Gitlab base_url | string | `""` | no | 23 | | gitlab\_token | Gitlab token | string | n/a | yes | 24 | 25 | ## Outputs 26 | 27 | | Name | Description | 28 | |------|-------------| 29 | | gitlab\_webhook\_secret | Gitlab webhook secret | 30 | | gitlab\_webhook\_urls | Gitlab webhook URL | 31 | 32 | 33 | -------------------------------------------------------------------------------- /terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | # VPC - use these parameters to create new VPC resources 2 | cidr = "10.10.0.0/16" 3 | 4 | azs = ["eu-west-1a", "eu-west-1b"] 5 | 6 | private_subnets = ["10.10.1.0/24", "10.10.2.0/24"] 7 | 8 | public_subnets = ["10.10.11.0/24", "10.10.12.0/24"] 9 | 10 | # VPC - use these parameters to use existing VPC resources 11 | # vpc_id = "vpc-1651acf1" 12 | # private_subnet_ids = ["subnet-1fe3d837", "subnet-129d66ab"] 13 | # public_subnet_ids = ["subnet-1211eef5", "subnet-163466ab"] 14 | 15 | # DNS 16 | route53_zone_name = "example.com" 17 | 18 | # ACM (SSL certificate) 19 | # Specify ARN of an existing certificate or new one will be created and validated using Route53 DNS: 20 | # certificate_arn = "arn:aws:acm:eu-west-1:135367859851:certificate/70e008e1-c0e1-4c7e-9670-7bb5bd4f5a84" 21 | 22 | # ECS Service and Task 23 | ecs_service_assign_public_ip = true 24 | 25 | # Atlantis 26 | atlantis_allowed_repo_names = ["awesome-repo", "another-awesome-repo"] 27 | atlantis_repo_whitelist = ["github.com/terraform-aws-modules/*"] 28 | 29 | # Specify one of the following block. 30 | # For Github 31 | atlantis_github_user = "" 32 | atlantis_github_user_token = "" 33 | 34 | # For Gitlab 35 | atlantis_gitlab_user = "" 36 | atlantis_gitlab_user_token = "" 37 | 38 | # For Bitbucket 39 | atlantis_bitbucket_user = "" 40 | atlantis_bitbucket_user_token = "" 41 | 42 | # Tags 43 | tags = { 44 | Name = "atlantis" 45 | } 46 | -------------------------------------------------------------------------------- /examples/github-repository-webhook/README.md: -------------------------------------------------------------------------------- 1 | # GitHub repository webhook for Atlantis 2 | 3 | Configuration in this directory creates GitHub repository webhooks configured to Atlantis URL. This example uses value of webhook secret which got generated when Atlantis setup by referring to `terraform.tfstate`, so this example has to run after Atlantis. 4 | 5 | GitHub's personal access token can be generated at https://github.com/settings/tokens 6 | 7 | ## Usage 8 | 9 | To run this code you need to copy `terraform.tfvars.sample` into `terraform.tfvars` and put your GitHub token and Github organization there or specify them using environment variables (`TF_VAR_github_token` and `TF_VAR_github_organization`). Once ready, execute: 10 | 11 | ```bash 12 | $ terraform init 13 | $ terraform plan 14 | $ terraform apply 15 | ``` 16 | 17 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 18 | 19 | 20 | ## Inputs 21 | 22 | | Name | Description | Type | Default | Required | 23 | |------|-------------|:----:|:-----:|:-----:| 24 | | github\_organization | Github organization | string | n/a | yes | 25 | | github\_token | Github token | string | n/a | yes | 26 | 27 | ## Outputs 28 | 29 | | Name | Description | 30 | |------|-------------| 31 | | github\_webhook\_secret | Github webhook secret | 32 | | github\_webhook\_urls | Github webhook URL | 33 | 34 | 35 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use on all resources created (VPC, ALB, etc)" 3 | default = "atlantis" 4 | } 5 | 6 | variable "tags" { 7 | description = "A map of tags to use on all resources" 8 | default = {} 9 | } 10 | 11 | # VPC 12 | variable "vpc_id" { 13 | description = "ID of an existing VPC where resources will be created" 14 | default = "" 15 | } 16 | 17 | variable "public_subnet_ids" { 18 | description = "A list of IDs of existing public subnets inside the VPC" 19 | type = "list" 20 | default = [] 21 | } 22 | 23 | variable "private_subnet_ids" { 24 | description = "A list of IDs of existing private subnets inside the VPC" 25 | type = "list" 26 | default = [] 27 | } 28 | 29 | variable "cidr" { 30 | description = "The CIDR block for the VPC which will be created if `vpc_id` is not specified" 31 | default = "" 32 | } 33 | 34 | variable "azs" { 35 | description = "A list of availability zones in the region" 36 | type = "list" 37 | default = [] 38 | } 39 | 40 | variable "public_subnets" { 41 | description = "A list of public subnets inside the VPC" 42 | type = "list" 43 | default = [] 44 | } 45 | 46 | variable "private_subnets" { 47 | description = "A list of private subnets inside the VPC" 48 | type = "list" 49 | default = [] 50 | } 51 | 52 | # ALB 53 | variable "alb_ingress_cidr_blocks" { 54 | description = "List of IPv4 CIDR ranges to use on all ingress rules of the ALB." 55 | type = "list" 56 | default = ["0.0.0.0/0"] 57 | } 58 | 59 | variable "alb_log_bucket_name" { 60 | description = "S3 bucket (externally created) for storing load balancer access logs. Required if alb_logging_enabled is true." 61 | type = "string" 62 | default = "" 63 | } 64 | 65 | variable "alb_log_location_prefix" { 66 | description = "S3 prefix within the log_bucket_name under which logs are stored." 67 | type = "string" 68 | default = "" 69 | } 70 | 71 | variable "alb_logging_enabled" { 72 | description = "Controls if the ALB will log requests to S3." 73 | type = "string" 74 | default = false 75 | } 76 | 77 | # ACM 78 | variable "certificate_arn" { 79 | description = "ARN of certificate issued by AWS ACM. If empty, a new ACM certificate will be created and validated using Route53 DNS" 80 | default = "" 81 | } 82 | 83 | variable "acm_certificate_domain_name" { 84 | description = "Route53 domain name to use for ACM certificate. Route53 zone for this domain should be created in advance. Specify if it is different from value in `route53_zone_name`" 85 | default = "" 86 | } 87 | 88 | # Route53 89 | variable "route53_zone_name" { 90 | description = "Route53 zone name to create ACM certificate in and main A-record, without trailing dot" 91 | default = "" 92 | } 93 | 94 | variable "create_route53_record" { 95 | description = "Whether to create Route53 record for Atlantis" 96 | default = true 97 | } 98 | 99 | # Cloudwatch 100 | variable "cloudwatch_log_retention_in_days" { 101 | description = "Retention period of Atlantis CloudWatch logs" 102 | default = 7 103 | } 104 | 105 | # SSM parameters for secrets 106 | variable "webhook_ssm_parameter_name" { 107 | description = "Name of SSM parameter to keep webhook secret" 108 | default = "/atlantis/webhook/secret" 109 | } 110 | 111 | variable "atlantis_github_user_token_ssm_parameter_name" { 112 | description = "Name of SSM parameter to keep atlantis_github_user_token" 113 | default = "/atlantis/github/user/token" 114 | } 115 | 116 | variable "atlantis_gitlab_user_token_ssm_parameter_name" { 117 | description = "Name of SSM parameter to keep atlantis_gitlab_user_token" 118 | default = "/atlantis/gitlab/user/token" 119 | } 120 | 121 | variable "atlantis_bitbucket_user_token_ssm_parameter_name" { 122 | description = "Name of SSM parameter to keep atlantis_bitbucket_user_token" 123 | default = "/atlantis/bitbucket/user/token" 124 | } 125 | 126 | variable "ssm_kms_key_arn" { 127 | description = "ARN of KMS key to use for entryption and decryption of SSM Parameters. Required only if your key uses a custom KMS key and not the default key" 128 | default = "" 129 | } 130 | 131 | # ECS Service / Task 132 | variable "ecs_service_assign_public_ip" { 133 | description = "Should be true, if ECS service is using public subnets (more info: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_cannot_pull_image.html)" 134 | default = false 135 | } 136 | 137 | variable "policies_arn" { 138 | description = "A list of the ARN of the policies you want to apply" 139 | type = "list" 140 | default = ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"] 141 | } 142 | 143 | variable "ecs_service_desired_count" { 144 | description = "The number of instances of the task definition to place and keep running" 145 | default = 1 146 | } 147 | 148 | variable "ecs_service_deployment_maximum_percent" { 149 | description = "The upper limit (as a percentage of the service's desiredCount) of the number of running tasks that can be running in a service during a deployment" 150 | default = 200 151 | } 152 | 153 | variable "ecs_service_deployment_minimum_healthy_percent" { 154 | description = "The lower limit (as a percentage of the service's desiredCount) of the number of running tasks that must remain running and healthy in a service during a deployment" 155 | default = 50 156 | } 157 | 158 | variable "ecs_task_cpu" { 159 | description = "The number of cpu units used by the task" 160 | default = 256 161 | } 162 | 163 | variable "ecs_task_memory" { 164 | description = "The amount (in MiB) of memory used by the task" 165 | default = 512 166 | } 167 | 168 | variable "container_memory_reservation" { 169 | description = "The amount of memory (in MiB) to reserve for the container" 170 | default = 128 171 | } 172 | 173 | variable "custom_container_definitions" { 174 | description = "A list of valid container definitions provided as a single valid JSON document. By default, the standard container definition is used." 175 | default = "" 176 | } 177 | 178 | # Atlantis 179 | variable "atlantis_image" { 180 | description = "Docker image to run Atlantis with. If not specified, official Atlantis image will be used" 181 | default = "" 182 | } 183 | 184 | variable "atlantis_version" { 185 | description = "Verion of Atlantis to run. If not specified latest will be used" 186 | default = "latest" 187 | } 188 | 189 | variable "atlantis_port" { 190 | description = "Local port Atlantis should be running on. Default value is most likely fine." 191 | default = "4141" 192 | } 193 | 194 | variable "atlantis_repo_whitelist" { 195 | description = "List of allowed repositories Atlantis can be used with" 196 | type = "list" 197 | } 198 | 199 | variable "atlantis_allowed_repo_names" { 200 | description = "Github repositories where webhook should be created" 201 | type = "list" 202 | default = [] 203 | } 204 | 205 | variable "allow_repo_config" { 206 | description = "When true allows the use of atlantis.yaml config files within the source repos." 207 | type = "string" 208 | default = "false" 209 | } 210 | 211 | # Github 212 | variable "atlantis_github_user" { 213 | description = "GitHub username that is running the Atlantis command" 214 | default = "" 215 | } 216 | 217 | variable "atlantis_github_user_token" { 218 | description = "GitHub token of the user that is running the Atlantis command" 219 | default = "" 220 | } 221 | 222 | # Gitlab 223 | variable "atlantis_gitlab_user" { 224 | description = "Gitlab username that is running the Atlantis command" 225 | default = "" 226 | } 227 | 228 | variable "atlantis_gitlab_user_token" { 229 | description = "Gitlab token of the user that is running the Atlantis command" 230 | default = "" 231 | } 232 | 233 | variable "atlantis_gitlab_hostname" { 234 | description = "Gitlab server hostname, defaults to gitlab.com" 235 | default = "gitlab.com" 236 | } 237 | 238 | # Bitbucket 239 | 240 | variable "atlantis_bitbucket_user" { 241 | description = "Bitbucket username that is running the Atlantis command" 242 | default = "" 243 | } 244 | 245 | variable "atlantis_bitbucket_user_token" { 246 | description = "Bitbucket token of the user that is running the Atlantis command" 247 | default = "" 248 | } 249 | 250 | variable "custom_environment_secrets" { 251 | description = "List of additional secrets the container will use (list should contain maps with `name` and `valueFrom`)" 252 | default = [] 253 | } 254 | 255 | variable "custom_environment_variables" { 256 | description = "List of additional environment variables the container will use (list should contain maps with `name` and `value`)" 257 | default = [] 258 | } 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Terraform module which runs Atlantis on AWS Fargate 2 | 3 | [Atlantis](https://www.runatlantis.io/) is tool which provides unified workflow for collaborating on Terraform through GitHub, GitLab and Bitbucket Cloud. 4 | 5 | This repository contains Terraform infrastructure code which creates AWS resources required to run [Atlantis](https://www.runatlantis.io/) on AWS, including: 6 | 7 | - Virtual Private Cloud (VPC) 8 | - SSL certificate using Amazon Certificate Manager (ACM) 9 | - Application Load Balancer (ALB) 10 | - Domain name using AWS Route53 which points to ALB 11 | - [AWS Elastic Cloud Service (ECS)](https://aws.amazon.com/ecs/) and [AWS Fargate](https://aws.amazon.com/fargate/) running Atlantis Docker image 12 | - AWS Parameter Store to keep secrets and access them in ECS task natively 13 | 14 | [AWS Fargate](https://aws.amazon.com/fargate/) is used instead of AWS ECS/EC2 to reduce the bill, and it is also a cool AWS service. 15 | 16 | Depending on which SCM system you use, Github repositories or Gitlab projects has to be configured to post events to Atlantis webhook URL. 17 | 18 | See `README.md` in `examples` for Github or Gitlab for complete details. 19 | 20 | For Bitbucket Cloud webhook configuration follow instructions in [the official Atlantis documentation](https://www.runatlantis.io/docs/configuring-webhooks.html#bitbucket-cloud-bitbucket-org-webhook). 21 | 22 | TODO: For Bitbucket Cloud an IP whitelist should be implemented for the webhook url as stated in [the official Atlantis documentation](https://www.runatlantis.io/docs/security.html#bitbucket-cloud-bitbucket-org) due to lack of support for webhook secrets. 23 | 24 | ### Before using Atlantis and the code in this repository please make sure that you have read and understood the security implications described in [the official Atlantis documentation](https://www.runatlantis.io/docs/security.html). 25 | 26 | ## How to use this? 27 | 28 | As often with the code published in [terraform-aws-modules GitHub organization](https://github.com/terraform-aws-modules) you should have everything to run this code and get Atlantis up and running. 29 | 30 | There are three ways to do this: 31 | 32 | 1. [As a standalone project](https://github.com/terraform-aws-modules/terraform-aws-atlantis#run-atlantis-as-a-standalone-project) 33 | 1. [As a Terraform module](https://github.com/terraform-aws-modules/terraform-aws-atlantis#run-atlantis-as-a-terraform-module) 34 | 1. [As a part of an existing AWS infrastructure](https://github.com/terraform-aws-modules/terraform-aws-atlantis#run-atlantis-as-a-part-of-an-existing-aws-infrastructure-use-existing-vpc) 35 | 36 | ### Run Atlantis as a standalone project 37 | 38 | 1. Clone this github repository: 39 | 40 | ``` 41 | $ git clone git@github.com:terraform-aws-modules/terraform-aws-atlantis.git 42 | $ cd terraform-aws-atlantis 43 | ``` 44 | 45 | 2. Copy sample `terraform.tfvars.sample` into `terraform.tfvars` and specify required variables there. 46 | 47 | 3. Run `terraform init` to download required providers and modules. 48 | 49 | 4. Run `terraform apply` to apply the Terraform configuration and create required infrastructure. 50 | 51 | 5. Run `terraform output atlantis_url` to get URL where Atlantis is publicly reachable. (Note: It may take a minute or two to get it reachable for the first time) 52 | 53 | 6. Github webhook is automatically created if `github_token`, `github_organization` and `github_repo_names` were specified. Read [Add GitHub Webhook](https://github.com/runatlantis/atlantis#add-github-webhook) in the official Atlantis documentation or check [example "GitHub repository webhook for Atlantis"](https://github.com/terraform-aws-modules/terraform-aws-atlantis/tree/master/examples/github-repository-webhook) to add more webhooks. 54 | 55 | ### Run Atlantis as a Terraform module 56 | 57 | This way allows integration with your existing Terraform configurations. 58 | 59 | ```hcl 60 | module "atlantis" { 61 | source = "terraform-aws-modules/atlantis/aws" 62 | 63 | name = "atlantis" 64 | 65 | # VPC 66 | cidr = "10.20.0.0/16" 67 | azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"] 68 | private_subnets = ["10.20.1.0/24", "10.20.2.0/24", "10.20.3.0/24"] 69 | public_subnets = ["10.20.101.0/24", "10.20.102.0/24", "10.20.103.0/24"] 70 | 71 | # DNS (without trailing dot) 72 | route53_zone_name = "example.com" 73 | 74 | # ACM (SSL certificate) - Specify ARN of an existing certificate or new one will be created and validated using Route53 DNS 75 | certificate_arn = "arn:aws:acm:eu-west-1:135367859851:certificate/70e008e1-c0e1-4c7e-9670-7bb5bd4f5a84" 76 | 77 | # Atlantis 78 | atlantis_github_user = "atlantis-bot" 79 | atlantis_github_user_token = "examplegithubtoken" 80 | atlantis_repo_whitelist = ["github.com/terraform-aws-modules/*"] 81 | } 82 | ``` 83 | 84 | ### Run Atlantis as a part of an existing AWS infrastructure (use existing VPC) 85 | 86 | This way allows integration with your existing AWS resources - VPC, public and private subnets. Specify the following arguments (see methods described above): 87 | 88 | ``` 89 | vpc_id = "vpc-1651acf1" 90 | private_subnet_ids = ["subnet-1fe3d837", "subnet-129d66ab"] 91 | public_subnet_ids = ["subnet-1211eef5", "subnet-163466ab"] 92 | ``` 93 | 94 | If `vpc_id` is specified it will take precedence over `cidr` and existing VPC will be used. `private_subnet_ids` and `public_subnet_ids` must be specified also. 95 | 96 | Make sure that both private and public subnets were created in the same set of availability zones (ALB will be created in public subnets, ECS Fargate service in private subnets). 97 | 98 | If all provided subnets are public (no NAT gateway) then `ecs_service_assign_public_ip` should be set to `true`. 99 | 100 | ## Notes 101 | 102 | 1. AWS Route53 zone is not created by this module, so zone specified as a value in `route53_zone_name` should be created before using this module. Check documentation for [aws_route53_zone](https://www.terraform.io/docs/providers/aws/r/route53_zone.html). 103 | 1. Currently this module configures Atlantis in a way that it can not be used to work with GitHub and Gitlab simultaneously (can't make list of ECS secrets conditionally). 104 | 105 | ## Examples 106 | 107 | * [GitHub repository webhook for Atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/tree/master/examples/github-repository-webhook) 108 | * [GitLab repository webhook for Atlantis](https://github.com/terraform-aws-modules/terraform-aws-atlantis/tree/master/examples/gitlab-repository-webhook) 109 | 110 | 111 | ## Inputs 112 | 113 | | Name | Description | Type | Default | Required | 114 | |------|-------------|:----:|:-----:|:-----:| 115 | | acm\_certificate\_domain\_name | Route53 domain name to use for ACM certificate. Route53 zone for this domain should be created in advance. Specify if it is different from value in `route53_zone_name` | string | `""` | no | 116 | | alb\_ingress\_cidr\_blocks | List of IPv4 CIDR ranges to use on all ingress rules of the ALB. | list | `[ "0.0.0.0/0" ]` | no | 117 | | alb\_log\_bucket\_name | S3 bucket (externally created) for storing load balancer access logs. Required if alb_logging_enabled is true. | string | `""` | no | 118 | | alb\_log\_location\_prefix | S3 prefix within the log_bucket_name under which logs are stored. | string | `""` | no | 119 | | alb\_logging\_enabled | Controls if the ALB will log requests to S3. | string | `"false"` | no | 120 | | allow\_repo\_config | When true allows the use of atlantis.yaml config files within the source repos. | string | `"false"` | no | 121 | | atlantis\_allowed\_repo\_names | Github repositories where webhook should be created | list | `[]` | no | 122 | | atlantis\_bitbucket\_user | Bitbucket username that is running the Atlantis command | string | `""` | no | 123 | | atlantis\_bitbucket\_user\_token | Bitbucket token of the user that is running the Atlantis command | string | `""` | no | 124 | | atlantis\_bitbucket\_user\_token\_ssm\_parameter\_name | Name of SSM parameter to keep atlantis_bitbucket_user_token | string | `"/atlantis/bitbucket/user/token"` | no | 125 | | atlantis\_github\_user | GitHub username that is running the Atlantis command | string | `""` | no | 126 | | atlantis\_github\_user\_token | GitHub token of the user that is running the Atlantis command | string | `""` | no | 127 | | atlantis\_github\_user\_token\_ssm\_parameter\_name | Name of SSM parameter to keep atlantis_github_user_token | string | `"/atlantis/github/user/token"` | no | 128 | | atlantis\_gitlab\_hostname | Gitlab server hostname, defaults to gitlab.com | string | `"gitlab.com"` | no | 129 | | atlantis\_gitlab\_user | Gitlab username that is running the Atlantis command | string | `""` | no | 130 | | atlantis\_gitlab\_user\_token | Gitlab token of the user that is running the Atlantis command | string | `""` | no | 131 | | atlantis\_gitlab\_user\_token\_ssm\_parameter\_name | Name of SSM parameter to keep atlantis_gitlab_user_token | string | `"/atlantis/gitlab/user/token"` | no | 132 | | atlantis\_image | Docker image to run Atlantis with. If not specified, official Atlantis image will be used | string | `""` | no | 133 | | atlantis\_port | Local port Atlantis should be running on. Default value is most likely fine. | string | `"4141"` | no | 134 | | atlantis\_repo\_whitelist | List of allowed repositories Atlantis can be used with | list | n/a | yes | 135 | | atlantis\_version | Verion of Atlantis to run. If not specified latest will be used | string | `"latest"` | no | 136 | | azs | A list of availability zones in the region | list | `[]` | no | 137 | | certificate\_arn | ARN of certificate issued by AWS ACM. If empty, a new ACM certificate will be created and validated using Route53 DNS | string | `""` | no | 138 | | cidr | The CIDR block for the VPC which will be created if `vpc_id` is not specified | string | `""` | no | 139 | | cloudwatch\_log\_retention\_in\_days | Retention period of Atlantis CloudWatch logs | string | `"7"` | no | 140 | | container\_memory\_reservation | The amount of memory (in MiB) to reserve for the container | string | `"128"` | no | 141 | | create\_route53\_record | Whether to create Route53 record for Atlantis | string | `"true"` | no | 142 | | custom\_container\_definitions | A list of valid container definitions provided as a single valid JSON document. By default, the standard container definition is used. | string | `""` | no | 143 | | custom\_environment\_secrets | List of additional secrets the container will use (list should contain maps with `name` and `valueFrom`) | list | `[]` | no | 144 | | custom\_environment\_variables | List of additional environment variables the container will use (list should contain maps with `name` and `value`) | list | `[]` | no | 145 | | ecs\_service\_assign\_public\_ip | Should be true, if ECS service is using public subnets (more info: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_cannot_pull_image.html) | string | `"false"` | no | 146 | | ecs\_service\_deployment\_maximum\_percent | The upper limit (as a percentage of the service's desiredCount) of the number of running tasks that can be running in a service during a deployment | string | `"200"` | no | 147 | | ecs\_service\_deployment\_minimum\_healthy\_percent | The lower limit (as a percentage of the service's desiredCount) of the number of running tasks that must remain running and healthy in a service during a deployment | string | `"50"` | no | 148 | | ecs\_service\_desired\_count | The number of instances of the task definition to place and keep running | string | `"1"` | no | 149 | | ecs\_task\_cpu | The number of cpu units used by the task | string | `"256"` | no | 150 | | ecs\_task\_memory | The amount (in MiB) of memory used by the task | string | `"512"` | no | 151 | | name | Name to use on all resources created (VPC, ALB, etc) | string | `"atlantis"` | no | 152 | | policies\_arn | A list of the ARN of the policies you want to apply | list | `[ "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" ]` | no | 153 | | private\_subnet\_ids | A list of IDs of existing private subnets inside the VPC | list | `[]` | no | 154 | | private\_subnets | A list of private subnets inside the VPC | list | `[]` | no | 155 | | public\_subnet\_ids | A list of IDs of existing public subnets inside the VPC | list | `[]` | no | 156 | | public\_subnets | A list of public subnets inside the VPC | list | `[]` | no | 157 | | route53\_zone\_name | Route53 zone name to create ACM certificate in and main A-record, without trailing dot | string | `""` | no | 158 | | ssm\_kms\_key\_arn | ARN of KMS key to use for entryption and decryption of SSM Parameters. Required only if your key uses a custom KMS key and not the default key | string | `""` | no | 159 | | tags | A map of tags to use on all resources | map | `{}` | no | 160 | | vpc\_id | ID of an existing VPC where resources will be created | string | `""` | no | 161 | | webhook\_ssm\_parameter\_name | Name of SSM parameter to keep webhook secret | string | `"/atlantis/webhook/secret"` | no | 162 | 163 | ## Outputs 164 | 165 | | Name | Description | 166 | |------|-------------| 167 | | alb\_dns\_name | Dns name of alb | 168 | | atlantis\_allowed\_repo\_names | Github repositories where webhook should be created | 169 | | atlantis\_url | URL of Atlantis | 170 | | atlantis\_url\_events | Webhook events URL of Atlantis | 171 | | task\_role\_arn | The Atlantis ECS task role arn | 172 | | vpc\_id | ID of the VPC that was created or passed in | 173 | | webhook\_secret | Webhook secret | 174 | 175 | 176 | 177 | ## Authors 178 | 179 | Module is created and maintained by [Anton Babenko](https://github.com/antonbabenko). 180 | 181 | [Seth Vargo](https://github.com/sethvargo) has created [atlantis-on-gke](https://github.com/sethvargo/atlantis-on-gke)(Terraform configurations for running Atlantis on [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine)). This inspired me to do similar stuff for AWS Fargate. 182 | 183 | ## License 184 | 185 | Apache 2 Licensed. See LICENSE for full details. 186 | 187 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # VPC - existing or new? 3 | vpc_id = "${var.vpc_id == "" ? module.vpc.vpc_id : var.vpc_id}" 4 | private_subnet_ids = "${coalescelist(module.vpc.private_subnets, var.private_subnet_ids)}" 5 | public_subnet_ids = "${coalescelist(module.vpc.public_subnets, var.public_subnet_ids)}" 6 | 7 | # Atlantis 8 | atlantis_image = "${var.atlantis_image == "" ? "runatlantis/atlantis:${var.atlantis_version}" : "${var.atlantis_image}" }" 9 | atlantis_url = "https://${coalesce(element(concat(aws_route53_record.atlantis.*.fqdn, list("")), 0), module.alb.dns_name)}" 10 | atlantis_url_events = "${local.atlantis_url}/events" 11 | 12 | # Include only one group of secrets - for github, gitlab or bitbucket 13 | has_secrets = "${var.atlantis_gitlab_user_token != "" || var.atlantis_github_user_token != "" || var.atlantis_bitbucket_user_token != ""}" 14 | 15 | secret_name_key = "${local.has_secrets ? (var.atlantis_gitlab_user_token != "" ? "ATLANTIS_GITLAB_TOKEN" : (var.atlantis_github_user_token != "" ? "ATLANTIS_GH_TOKEN" : "ATLANTIS_BITBUCKET_TOKEN")) : "unknown_secret_name_key"}" 16 | 17 | secret_name_value_from = "${local.has_secrets ? (var.atlantis_gitlab_user_token != "" ? var.atlantis_gitlab_user_token_ssm_parameter_name : (var.atlantis_github_user_token != "" ? var.atlantis_github_user_token_ssm_parameter_name : var.atlantis_bitbucket_user_token_ssm_parameter_name)) : "unknown_secret_name_value"}" 18 | 19 | secret_webhook_key = "${local.has_secrets ? (var.atlantis_gitlab_user_token != "" ? "ATLANTIS_GITLAB_WEBHOOK_SECRET" : (var.atlantis_github_user_token != "" ? "ATLANTIS_GH_WEBHOOK_SECRET" : "ATLANTIS_BITBUCKET_WEBHOOK_SECRET")) : "unknown_secret_webhook_key"}" 20 | 21 | # Container definitions 22 | container_definitions = "${var.custom_container_definitions == "" ? (var.atlantis_bitbucket_user_token != "" ? module.container_definition_bitbucket.json : module.container_definition_github_gitlab.json) : var.custom_container_definitions}" 23 | 24 | container_definition_environment = [ 25 | { 26 | name = "ATLANTIS_ALLOW_REPO_CONFIG" 27 | value = "${var.allow_repo_config}" 28 | }, 29 | { 30 | name = "ATLANTIS_GITLAB_HOSTNAME" 31 | value = "${var.atlantis_gitlab_hostname}" 32 | }, 33 | { 34 | name = "ATLANTIS_LOG_LEVEL" 35 | value = "debug" 36 | }, 37 | { 38 | name = "ATLANTIS_PORT" 39 | value = "${var.atlantis_port}" 40 | }, 41 | { 42 | name = "ATLANTIS_ATLANTIS_URL" 43 | value = "${local.atlantis_url}" 44 | }, 45 | { 46 | name = "ATLANTIS_GH_USER" 47 | value = "${var.atlantis_github_user}" 48 | }, 49 | { 50 | name = "ATLANTIS_GITLAB_USER" 51 | value = "${var.atlantis_gitlab_user}" 52 | }, 53 | { 54 | name = "ATLANTIS_BITBUCKET_USER" 55 | value = "${var.atlantis_bitbucket_user}" 56 | }, 57 | { 58 | name = "ATLANTIS_REPO_WHITELIST" 59 | value = "${join(",", var.atlantis_repo_whitelist)}" 60 | }, 61 | ] 62 | 63 | # Secret access tokens 64 | container_definition_secrets_1 = [ 65 | { 66 | name = "${local.secret_name_key}" 67 | valueFrom = "${local.secret_name_value_from}" 68 | }, 69 | ] 70 | 71 | # Webhook secrets are not supported by BitBucket 72 | container_definition_secrets_2 = [ 73 | { 74 | name = "${local.secret_webhook_key}" 75 | valueFrom = "${var.webhook_ssm_parameter_name}" 76 | }, 77 | ] 78 | 79 | tags = "${merge(map("Name", var.name), var.tags)}" 80 | } 81 | 82 | data "aws_region" "current" {} 83 | 84 | data "aws_caller_identity" "current" {} 85 | 86 | data "aws_route53_zone" "this" { 87 | count = "${var.create_route53_record}" 88 | 89 | name = "${var.route53_zone_name}" 90 | private_zone = false 91 | } 92 | 93 | ################### 94 | # Secret for webhook 95 | ################### 96 | resource "random_id" "webhook" { 97 | byte_length = "64" 98 | } 99 | 100 | resource "aws_ssm_parameter" "webhook" { 101 | count = "${var.atlantis_bitbucket_user_token != "" ? 0 : 1}" 102 | 103 | name = "${var.webhook_ssm_parameter_name}" 104 | type = "SecureString" 105 | value = "${random_id.webhook.hex}" 106 | } 107 | 108 | resource "aws_ssm_parameter" "atlantis_github_user_token" { 109 | count = "${var.atlantis_github_user_token != "" ? 1 : 0}" 110 | 111 | name = "${var.atlantis_github_user_token_ssm_parameter_name}" 112 | type = "SecureString" 113 | value = "${var.atlantis_github_user_token}" 114 | } 115 | 116 | resource "aws_ssm_parameter" "atlantis_gitlab_user_token" { 117 | count = "${var.atlantis_gitlab_user_token != "" ? 1 : 0}" 118 | 119 | name = "${var.atlantis_gitlab_user_token_ssm_parameter_name}" 120 | type = "SecureString" 121 | value = "${var.atlantis_gitlab_user_token}" 122 | } 123 | 124 | resource "aws_ssm_parameter" "atlantis_bitbucket_user_token" { 125 | count = "${var.atlantis_bitbucket_user_token != "" ? 1 : 0}" 126 | 127 | name = "${var.atlantis_bitbucket_user_token_ssm_parameter_name}" 128 | type = "SecureString" 129 | value = "${var.atlantis_bitbucket_user_token}" 130 | } 131 | 132 | ################### 133 | # VPC 134 | ################### 135 | module "vpc" { 136 | source = "terraform-aws-modules/vpc/aws" 137 | version = "v1.53.0" 138 | 139 | create_vpc = "${var.vpc_id == ""}" 140 | 141 | name = "${var.name}" 142 | 143 | cidr = "${var.cidr}" 144 | azs = "${var.azs}" 145 | private_subnets = "${var.private_subnets}" 146 | public_subnets = "${var.public_subnets}" 147 | 148 | enable_nat_gateway = true 149 | single_nat_gateway = true 150 | 151 | tags = "${local.tags}" 152 | } 153 | 154 | ################### 155 | # ALB 156 | ################### 157 | module "alb" { 158 | source = "terraform-aws-modules/alb/aws" 159 | version = "v3.5.0" 160 | 161 | load_balancer_name = "${var.name}" 162 | 163 | vpc_id = "${local.vpc_id}" 164 | subnets = ["${local.public_subnet_ids}"] 165 | security_groups = ["${module.alb_https_sg.this_security_group_id}", "${module.alb_http_sg.this_security_group_id}"] 166 | 167 | logging_enabled = "${var.alb_logging_enabled}" 168 | log_bucket_name = "${var.alb_log_bucket_name}" 169 | log_location_prefix = "${var.alb_log_location_prefix}" 170 | 171 | https_listeners = [{ 172 | port = 443 173 | certificate_arn = "${var.certificate_arn == "" ? module.acm.this_acm_certificate_arn : var.certificate_arn}" 174 | }] 175 | 176 | https_listeners_count = 1 177 | 178 | http_tcp_listeners = [{ 179 | port = 80 180 | protocol = "HTTP" 181 | }] 182 | 183 | http_tcp_listeners_count = 1 184 | 185 | target_groups = [{ 186 | name = "${var.name}" 187 | backend_protocol = "HTTP" 188 | backend_port = "${var.atlantis_port}" 189 | target_type = "ip" 190 | deregistration_delay = 10 191 | }] 192 | 193 | target_groups_count = 1 194 | 195 | tags = "${local.tags}" 196 | } 197 | 198 | resource "aws_lb_listener_rule" "redirect_http_to_https" { 199 | listener_arn = "${module.alb.http_tcp_listener_arns[0]}" 200 | 201 | action { 202 | type = "redirect" 203 | 204 | redirect { 205 | port = "443" 206 | protocol = "HTTPS" 207 | status_code = "HTTP_301" 208 | } 209 | } 210 | 211 | condition { 212 | field = "path-pattern" 213 | values = ["*"] 214 | } 215 | } 216 | 217 | ################### 218 | # Security groups 219 | ################### 220 | module "alb_https_sg" { 221 | source = "terraform-aws-modules/security-group/aws//modules/https-443" 222 | version = "v2.11.0" 223 | 224 | name = "${var.name}-alb-https" 225 | vpc_id = "${local.vpc_id}" 226 | description = "Security group with HTTPS ports open for specific IPv4 CIDR block (or everybody), egress ports are all world open" 227 | 228 | ingress_cidr_blocks = "${var.alb_ingress_cidr_blocks}" 229 | 230 | tags = "${local.tags}" 231 | } 232 | 233 | module "alb_http_sg" { 234 | source = "terraform-aws-modules/security-group/aws//modules/http-80" 235 | version = "v2.9.0" 236 | 237 | name = "${var.name}-alb-http" 238 | vpc_id = "${local.vpc_id}" 239 | description = "Security group with HTTP ports open for specific IPv4 CIDR block (or everybody), egress ports are all world open" 240 | 241 | ingress_cidr_blocks = "${var.alb_ingress_cidr_blocks}" 242 | 243 | tags = "${local.tags}" 244 | } 245 | 246 | module "atlantis_sg" { 247 | source = "terraform-aws-modules/security-group/aws" 248 | version = "v2.11.0" 249 | 250 | name = "${var.name}" 251 | vpc_id = "${local.vpc_id}" 252 | description = "Security group with open port for Atlantis (${var.atlantis_port}) from ALB, egress ports are all world open" 253 | 254 | computed_ingress_with_source_security_group_id = [ 255 | { 256 | from_port = "${var.atlantis_port}" 257 | to_port = "${var.atlantis_port}" 258 | protocol = "tcp" 259 | description = "Atlantis" 260 | source_security_group_id = "${module.alb_https_sg.this_security_group_id}" 261 | }, 262 | ] 263 | 264 | number_of_computed_ingress_with_source_security_group_id = 1 265 | 266 | egress_rules = ["all-all"] 267 | 268 | tags = "${local.tags}" 269 | } 270 | 271 | ################### 272 | # ACM (SSL certificate) 273 | ################### 274 | module "acm" { 275 | source = "terraform-aws-modules/acm/aws" 276 | version = "v1.1.0" 277 | 278 | create_certificate = "${var.certificate_arn == "" ? 1 : 0}" 279 | 280 | domain_name = "${var.acm_certificate_domain_name == "" ? join(".", list(var.name, var.route53_zone_name)) : var.acm_certificate_domain_name}" 281 | 282 | zone_id = "${var.certificate_arn == "" ? element(concat(data.aws_route53_zone.this.*.id, list("")), 0) : ""}" 283 | 284 | tags = "${local.tags}" 285 | } 286 | 287 | ################### 288 | # Route53 record 289 | ################### 290 | resource "aws_route53_record" "atlantis" { 291 | count = "${var.create_route53_record}" 292 | 293 | zone_id = "${data.aws_route53_zone.this.zone_id}" 294 | name = "${var.name}" 295 | type = "A" 296 | 297 | alias { 298 | name = "${module.alb.dns_name}" 299 | zone_id = "${module.alb.load_balancer_zone_id}" 300 | evaluate_target_health = true 301 | } 302 | } 303 | 304 | ################### 305 | # ECS 306 | ################### 307 | module "ecs" { 308 | source = "terraform-aws-modules/ecs/aws" 309 | version = "v1.1.0" 310 | 311 | name = "${var.name}" 312 | } 313 | 314 | resource "aws_iam_role" "ecs_task_execution" { 315 | name = "${var.name}-ecs_task_execution" 316 | 317 | assume_role_policy = <