Skip to content

borissavelev/terraform-aws-atlantis

 
 

Repository files navigation

AWS Terraform module which runs Atlantis on AWS Fargate

Atlantis is tool which provides unified workflow for collaborating on Terraform through GitHub, GitLab and Bitbucket Cloud.

This repository contains Terraform infrastructure code which creates AWS resources required to run Atlantis on AWS, including:

  • Virtual Private Cloud (VPC)
  • SSL certificate using Amazon Certificate Manager (ACM)
  • Application Load Balancer (ALB)
  • Domain name using AWS Route53 which points to ALB
  • AWS Elastic Cloud Service (ECS) and AWS Fargate running Atlantis Docker image
  • AWS Parameter Store to keep secrets and access them in ECS task natively

AWS Fargate with optional support for Fargate Spot is used to reduce the bill, and it is also a cool AWS service.

Depending on which SCM system you use, Github repositories or Gitlab projects has to be configured to post events to Atlantis webhook URL.

See README.md in examples for Github or Gitlab for complete details.

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.

How to use this?

As often with the code published in terraform-aws-modules GitHub organization you should have everything to run this code and get Atlantis up and running.

There are three ways to do this:

  1. As a standalone project
  2. As a Terraform module
  3. As a part of an existing AWS infrastructure

Run Atlantis as a standalone project

  1. Clone this github repository:
$ git clone git@github.com:terraform-aws-modules/terraform-aws-atlantis.git
$ cd terraform-aws-atlantis
  1. Copy sample terraform.tfvars.sample into terraform.tfvars and specify required variables there.

  2. Run terraform init to download required providers and modules.

  3. Run terraform apply to apply the Terraform configuration and create required infrastructure.

  4. 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)

  5. Github webhook is automatically created if github_token, github_owner and github_repo_names were specified. Read Add GitHub Webhook in the official Atlantis documentation or check example "GitHub repository webhook for Atlantis" to add more webhooks.

Run Atlantis as a Terraform module

This way allows integration with your existing Terraform configurations.

module "atlantis" {
  source  = "terraform-aws-modules/atlantis/aws"
  version = "~> 3.0"

  name = "atlantis"

  # VPC
  cidr            = "10.20.0.0/16"
  azs             = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
  private_subnets = ["10.20.1.0/24", "10.20.2.0/24", "10.20.3.0/24"]
  public_subnets  = ["10.20.101.0/24", "10.20.102.0/24", "10.20.103.0/24"]

  # DNS (without trailing dot)
  route53_zone_name = "example.com"

  # ACM (SSL certificate) - Specify ARN of an existing certificate or new one will be created and validated using Route53 DNS
  certificate_arn = "arn:aws:acm:eu-west-1:135367859851:certificate/70e008e1-c0e1-4c7e-9670-7bb5bd4f5a84"

  # Atlantis
  atlantis_github_user       = "atlantis-bot"
  atlantis_github_user_token = "examplegithubtoken"
  atlantis_repo_allowlist    = ["github.com/terraform-aws-modules/*"]
}

Provide Atlantis with server yaml configuration

server-atlantis.yaml

repos:
  - id: /.*/
    allow_custom_workflows: true
    allowed_overrides:
      - apply_requirements
      - workflow
    apply_requirements:
      - approved
    workflow: default

main.tf

module "atlantis" {
  source  = "terraform-aws-modules/atlantis/aws"

  # ...

  custom_environment_variables = [
    {
      name : "ATLANTIS_REPO_CONFIG_JSON",
      value : jsonencode(yamldecode(file("${path.module}/server-atlantis.yaml"))),
    },
  ]

  # ...
}

Run Atlantis as a part of an existing AWS infrastructure (use existing VPC)

This way allows integration with your existing AWS resources - VPC, public and private subnets. Specify the following arguments (see methods described above):

vpc_id             = "vpc-1651acf1"
private_subnet_ids = ["subnet-1fe3d837", "subnet-129d66ab"]
public_subnet_ids  = ["subnet-1211eef5", "subnet-163466ab"]

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.

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).

If all provided subnets are public (no NAT gateway) then ecs_service_assign_public_ip should be set to true.

Secure Atlantis with ALB Built-in Authentication

OpenID Connect (OIDC)

You can use service like Auth0 to secure access to Atlantis and require authentication on ALB. To enable this, you need to create Auth0 application and provide correct arguments to Atlantis module. Make sure to update application hostname, client id and client secret:

Read more in this post.

Auth0
alb_authenticate_oidc = {
  issuer                              = "https://youruser.eu.auth0.com/"
  token_endpoint                      = "https://youruser.eu.auth0.com/oauth/token"
  user_info_endpoint                  = "https://youruser.eu.auth0.com/userinfo"
  authorization_endpoint              = "https://youruser.eu.auth0.com/authorize"
  authentication_request_extra_params = {}
  client_id                           = "clientid"
  client_secret                       = "secret123" # a data source would be good here
}
Okta
alb_authenticate_oidc = {
  issuer                              = "https://dev-42069.okta.com/"
  token_endpoint                      = "https://dev-42069.okta.com/oauth2/v1/token"
  user_info_endpoint                  = "https://dev-42069.okta.com/oauth2/v1/userinfo"
  authorization_endpoint              = "https://dev-42069.okta.com/oauth2/v1/authorize"
  authentication_request_extra_params = {}
  client_id                           = "clientid"
  client_secret                       = "secret123" # a data source would be good here
}

Read more in this post

Google
  alb_authenticate_oidc = {
    issuer                              = "https://accounts.google.com"
    token_endpoint                      = "https://oauth2.googleapis.com/token"
    user_info_endpoint                  = "https://openidconnect.googleapis.com/v1/userinfo"
    authorization_endpoint              = "https://accounts.google.com/o/oauth2/v2/auth"
    authentication_request_extra_params = {}
    client_id                           = "google_client_id"
    client_secret                       = "google_client_secret"
  }

Note: remember to set your google consent screen to internal to only allow users from your own domain.

AWS Cognito with SAML

The AWS Cognito service allows you to define SAML applications tied to an identity provider (e.g., GSuite). The Atlantis ALB can then be configured to require an authenticated user managed by your identity provider.

To configure AWS Cognito connecting to a GSuite SAML application, you can use the gsuite-saml-cognito Terraform module.

To enable Cognito authentication on the Atlantis ALB, specify the following arguments containing attributes from your Cognito configuration.

alb_authenticate_cognito = {
  user_pool_arn       = "arn:aws:cognito-idp:us-west-2:1234567890:userpool/us-west-2_aBcDeFG"
  user_pool_client_id = "clientid123"
  user_pool_domain    = "sso.your-corp.com"
}

Allow GitHub Webhooks Unauthenticated Access

If you are using one of the authentication methods above along with managed GitHub (not self-hosted enterprise version), you'll need to allow unauthenticated access to GitHub's Webhook static IPs:

allow_unauthenticated_access = true
allow_github_webhooks        = true

Notes

  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.
  2. 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).
  3. For Bitbucket Cloud webhook configuration follow instructions in the official Atlantis documentation.

Examples

Requirements

Name Version
terraform >= 0.13.1
aws ~> 3.45
random >= 2.0

Providers

Name Version
aws ~> 3.45
random >= 2.0

Modules

Name Source Version
acm terraform-aws-modules/acm/aws v3.2.0
alb terraform-aws-modules/alb/aws v6.5.0
alb_http_sg terraform-aws-modules/security-group/aws//modules/http-80 v4.3.0
alb_https_sg terraform-aws-modules/security-group/aws//modules/https-443 v4.3.0
atlantis_sg terraform-aws-modules/security-group/aws v4.3.0
container_definition_bitbucket cloudposse/ecs-container-definition/aws v0.58.1
container_definition_github_gitlab cloudposse/ecs-container-definition/aws v0.58.1
ecs terraform-aws-modules/ecs/aws v3.3.0
efs_sg terraform-aws-modules/security-group/aws//modules/nfs v4.8.0
vpc terraform-aws-modules/vpc/aws v3.6.0

Resources

Name Type
aws_cloudwatch_log_group.atlantis resource
aws_ecs_service.atlantis resource
aws_ecs_task_definition.atlantis resource
aws_efs_access_point.this resource
aws_efs_file_system.this resource
aws_efs_mount_target.this resource
aws_iam_role.ecs_task_execution resource
aws_iam_role_policy.ecs_task_access_secrets resource
aws_iam_role_policy_attachment.ecs_task_execution resource
aws_lb_listener_rule.unauthenticated_access_for_cidr_blocks resource
aws_lb_listener_rule.unauthenticated_access_for_webhook resource
aws_route53_record.atlantis resource
aws_ssm_parameter.atlantis_bitbucket_user_token resource
aws_ssm_parameter.atlantis_github_user_token resource
aws_ssm_parameter.atlantis_gitlab_user_token resource
aws_ssm_parameter.webhook resource
random_id.webhook resource
aws_ecs_task_definition.atlantis data source
aws_iam_policy_document.ecs_task_access_secrets data source
aws_iam_policy_document.ecs_task_access_secrets_with_kms data source
aws_iam_policy_document.ecs_tasks data source
aws_partition.current data source
aws_region.current data source
aws_route53_zone.this data source

Inputs

Name Description Type Default Required
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
alb_authenticate_cognito Map of AWS Cognito authentication parameters to protect ALB (eg, using SAML). See https://www.terraform.io/docs/providers/aws/r/lb_listener.html#authenticate-cognito-action any {} no
alb_authenticate_oidc Map of Authenticate OIDC parameters to protect ALB (eg, using Auth0). See https://www.terraform.io/docs/providers/aws/r/lb_listener.html#authenticate-oidc-action any {} no
alb_drop_invalid_header_fields Indicates whether invalid header fields are dropped in application load balancers. Defaults to false. bool null no
alb_enable_deletion_protection If true, deletion of the load balancer will be disabled via the AWS API. This will prevent Terraform from deleting the load balancer. Defaults to false. bool null no
alb_http_security_group_tags Additional tags to put on the http security group map(string) {} no
alb_https_security_group_tags Additional tags to put on the https security group map(string) {} no
alb_ingress_cidr_blocks List of IPv4 CIDR ranges to use on all ingress rules of the ALB. list(string)
[
"0.0.0.0/0"
]
no
alb_listener_ssl_policy_default The security policy if using HTTPS externally on the load balancer. See. string "ELBSecurityPolicy-2016-08" no
alb_log_bucket_name S3 bucket (externally created) for storing load balancer access logs. Required if alb_logging_enabled is true. string "" no
alb_log_location_prefix S3 prefix within the log_bucket_name under which logs are stored. string "" no
alb_logging_enabled Controls if the ALB will log requests to S3. bool false no
allow_github_webhooks Whether to allow access for GitHub webhooks bool false no
allow_repo_config When true allows the use of atlantis.yaml config files within the source repos. string "false" no
allow_unauthenticated_access Whether to create ALB listener rule to allow unauthenticated access for certain CIDR blocks (eg. allow GitHub webhooks to bypass OIDC authentication) bool false no
allow_unauthenticated_access_priority ALB listener rule priority for allow unauthenticated access rule number 10 no
allow_unauthenticated_webhook_access_priority ALB listener rule priority for allow unauthenticated webhook access rule number 15 no
atlantis_bitbucket_base_url Base URL of Bitbucket Server, use for Bitbucket on prem (Stash) string "" no
atlantis_bitbucket_user Bitbucket username that is running the Atlantis command string "" no
atlantis_bitbucket_user_token Bitbucket token of the user that is running the Atlantis command string "" no
atlantis_bitbucket_user_token_ssm_parameter_name Name of SSM parameter to keep atlantis_bitbucket_user_token string "/atlantis/bitbucket/user/token" no
atlantis_fqdn FQDN of Atlantis to use. Set this only to override Route53 and ALB's DNS name. string null no
atlantis_github_user GitHub username that is running the Atlantis command string "" no
atlantis_github_user_token GitHub token of the user that is running the Atlantis command string "" no
atlantis_github_user_token_ssm_parameter_name Name of SSM parameter to keep atlantis_github_user_token string "/atlantis/github/user/token" no
atlantis_github_webhook_secret GitHub webhook secret of an app that is running the Atlantis command string "" no
atlantis_gitlab_hostname Gitlab server hostname, defaults to gitlab.com string "gitlab.com" no
atlantis_gitlab_user Gitlab username that is running the Atlantis command string "" no
atlantis_gitlab_user_token Gitlab token of the user that is running the Atlantis command string "" no
atlantis_gitlab_user_token_ssm_parameter_name Name of SSM parameter to keep atlantis_gitlab_user_token string "/atlantis/gitlab/user/token" no
atlantis_hide_prev_plan_comments Enables atlantis server --hide-prev-plan-comments hiding previous plan comments on update string "false" no
atlantis_image Docker image to run Atlantis with. If not specified, official Atlantis image will be used string "" no
atlantis_log_level Log level that Atlantis will run with. Accepted values are: <debug|info|warn|error> string "debug" no
atlantis_port Local port Atlantis should be running on. Default value is most likely fine. number 4141 no
atlantis_repo_allowlist List of allowed repositories Atlantis can be used with list(string) n/a yes
atlantis_security_group_tags Additional tags to put on the atlantis security group map(string) {} no
atlantis_version Verion of Atlantis to run. If not specified latest will be used string "latest" no
azs A list of availability zones in the region list(string) [] no
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
cidr The CIDR block for the VPC which will be created if vpc_id is not specified string "" no
cloudwatch_log_retention_in_days Retention period of Atlantis CloudWatch logs number 7 no
cloudwatch_logs_kms_key_id The ARN of the KMS Key to use when encrypting log data. string null no
command The command that is passed to the container list(string) null no
container_cpu The number of cpu units used by the atlantis container. If not specified ecs_task_cpu will be used number null no
container_depends_on The dependencies defined for container startup and shutdown. A container can contain multiple dependencies. When a dependency is defined for container startup, for container shutdown it is reversed. The condition can be one of START, COMPLETE, SUCCESS or HEALTHY
list(object({
containerName = string
condition = string
}))
null no
container_memory The amount (in MiB) of memory used by the atlantis container. If not specified ecs_task_memory will be used number null no
container_memory_reservation The amount of memory (in MiB) to reserve for the container number 128 no
create_ecs_cluster Whether to create an ECS cluster or not bool true no
create_route53_record Whether to create Route53 record for Atlantis bool true no
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
custom_environment_secrets List of additional secrets the container will use (list should contain maps with name and valueFrom)
list(object(
{
name = string
valueFrom = string
}
))
[] no
custom_environment_variables List of additional environment variables the container will use (list should contain maps with name and value)
list(object(
{
name = string
value = string
}
))
[] no
default_security_group_egress List of maps of egress rules to set on the default security group list(map(string)) [] no
default_security_group_ingress List of maps of ingress rules to set on the default security group list(map(string)) [] no
docker_labels The configuration options to send to the docker_labels map(string) null no
ecs_cluster_id ID of an existing ECS cluster where resources will be created string "" no
ecs_container_insights Controls if ECS Cluster has container insights enabled bool false no
ecs_fargate_spot Whether to run ECS Fargate Spot or not bool false no
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) bool false no
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 number 100 no
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 number 0 no
ecs_service_desired_count The number of instances of the task definition to place and keep running number 1 no
ecs_service_enable_execute_command Enable ECS exec for the service. This can be used to allow interactive sessions and commands to be executed in the container bool true no
ecs_service_force_new_deployment Enable to force a new task deployment of the service. This can be used to update tasks to use a newer Docker image with same image/tag combination (e.g. myimage:latest) bool false no
ecs_service_platform_version The platform version on which to run your service string "LATEST" no
ecs_task_cpu The number of cpu units used by the task number 256 no
ecs_task_memory The amount (in MiB) of memory used by the task number 512 no
enable_ecs_managed_tags Specifies whether to enable Amazon ECS managed tags for the tasks within the service bool false no
enable_ephemeral_storage Enable to use Fargate Ephemeral Storage bool false no
entrypoint The entry point that is passed to the container list(string) null no
ephemeral_storage_size Size of Ephemeral Storage in GiB number 21 no
essential Determines whether all other containers in a task are stopped, if this container fails or stops for any reason. Due to how Terraform type casts booleans in json it is required to double quote this value bool true no
external_task_definition_updates Enable to allow the task definition to be updated outside of this Terraform module. This should be enabled when using a deployment tool such as ecs-deploy which updates the task definition and will then keep the ECS service using the latest version of the task definition. bool false no
extra_container_definitions A list of valid container definitions provided as a single valid JSON document. These will be provided as supplimentary to the main Atlantis container definition list(any) [] no
extra_load_balancers A list of maps for additional ECS task load balancers list(map(string)) [] no
firelens_configuration The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more details, see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_FirelensConfiguration.html
object({
type = string
options = map(string)
})
null no
github_webhooks_cidr_blocks List of CIDR blocks used by GitHub webhooks list(string)
[
"140.82.112.0/20",
"185.199.108.0/22",
"192.30.252.0/22",
"143.55.64.0/20"
]
no
internal Whether the load balancer is internal or external bool false no
manage_default_security_group Should be true to adopt and manage default security group bool false no
mount_points Container mount points. This is a list of maps, where each map should contain a containerPath and sourceVolume. The readOnly key is optional. list(any) [] no
name Name to use on all resources created (VPC, ALB, etc) string "atlantis" no
permissions_boundary If provided, all IAM roles will be created with this permissions boundary attached. string null no
policies_arn A list of the ARN of the policies you want to apply list(string) null no
private_subnet_ids A list of IDs of existing private subnets inside the VPC list(string) [] no
private_subnets A list of private subnets inside the VPC list(string) [] no
propagate_tags Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION string null no
public_subnet_ids A list of IDs of existing public subnets inside the VPC list(string) [] no
public_subnets A list of public subnets inside the VPC list(string) [] no
readonly_root_filesystem Determines whether a container is given read-only access to its root filesystem. Due to how Terraform type casts booleans in json it is required to double quote this value bool false no
repository_credentials Container repository credentials; required when using a private repo. This map currently supports a single key; "credentialsParameter", which should be the ARN of a Secrets Manager's secret holding the credentials map(string) null no
route53_private_zone Enable to use a private Route53 zone bool false no
route53_record_name Name of Route53 record to create ACM certificate in and main A-record. If null is specified, var.name is used instead. Provide empty string to point root domain name to ALB. string null no
route53_zone_name Route53 zone name to create ACM certificate in and main A-record, without trailing dot string "" no
security_group_ids List of one or more security groups to be added to the load balancer list(string) [] no
ssm_kms_key_arn ARN of KMS key to use for encryption and decryption of SSM Parameters. Required only if your key uses a custom KMS key and not the default key string "" no
start_timeout Time duration (in seconds) to wait before giving up on resolving dependencies for a container number 30 no
stop_timeout Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own number 30 no
tags A map of tags to use on all resources map(string) {} no
trusted_entities A list of users or roles, that can assume the task role list(string) [] no
trusted_principals A list of principals, in addition to ecs-tasks.amazonaws.com, that can assume the task role list(string) [] no
ulimits Container ulimit settings. This is a list of maps, where each map should contain "name", "hardLimit" and "softLimit"
list(object({
name = string
hardLimit = number
softLimit = number
}))
null no
use_ecs_old_arn_format A flag to enable/disable tagging the ecs resources that require the new longer arn format bool false no
user The user to run as inside the container. Must be in the uid:gid or the default (null) will use the container's configured USER directive or root if not set. string null no
volumes_from A list of VolumesFrom maps which contain "sourceContainer" (name of the container that has the volumes to mount) and "readOnly" (whether the container can write to the volume)
list(object({
sourceContainer = string
readOnly = bool
}))
[] no
vpc_id ID of an existing VPC where resources will be created string "" no
webhook_ssm_parameter_name Name of SSM parameter to keep webhook secret string "/atlantis/webhook/secret" no
whitelist_unauthenticated_cidr_blocks List of allowed CIDR blocks to bypass authentication list(string) [] no
working_directory The working directory to run commands inside the container string null no

Outputs

Name Description
alb_arn ARN of alb
alb_dns_name Dns name of alb
alb_http_listeners_arn ARNs of alb http listeners
alb_http_listeners_id Ids of alb http listeners
alb_https_listeners_arn ARN of alb https listeners
alb_https_listeners_id Ids of alb https listeners
alb_security_group_id Security group of alb
alb_zone_id Zone ID of alb
atlantis_repo_allowlist Git repositories where webhook should be created
atlantis_url URL of Atlantis
atlantis_url_events Webhook events URL of Atlantis
ecs_cluster_arn ECS cluster ARN
ecs_cluster_id ECS cluster id
ecs_security_group Security group assigned to ECS Service in network configuration
ecs_task_definition Task definition for ECS service (used for external triggers)
private_subnet_ids IDs of the VPC private subnets that were created or passed in
public_subnet_ids IDs of the VPC public subnets that were created or passed in
task_role_arn The Atlantis ECS task role arn
task_role_id The Atlantis ECS task role id
task_role_name The Atlantis ECS task role name
task_role_unique_id The stable and unique string identifying the Atlantis ECS task role.
vpc_id ID of the VPC that was created or passed in
webhook_secret Webhook secret

Authors

Module is maintained by Anton Babenko with help from these awesome contributors.

License

Apache 2 Licensed. See LICENSE for full details.

About

Terraform configurations for running Atlantis on AWS Fargate. Github, Gitlab and BitBucket are supported

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • HCL 100.0%