├── env └── dev │ ├── logs-logzio.zip │ ├── autoscale-time.zip │ ├── no-alb.tf │ ├── discovery.tf │ ├── nsg.tf │ ├── fargate-create.yml │ ├── main.tf │ ├── variables.tf │ ├── role.tf │ ├── cicd.tf │ ├── logs-logzio.tf │ ├── ecs.tf │ ├── autoscale-time.tf │ ├── autoscale-perf.tf │ └── README.md ├── base ├── service_discovery_zone.tf ├── state.tf ├── main.tf ├── variables.tf ├── ecr.tf └── README.md ├── README.md └── LICENSE /env/dev/logs-logzio.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turnerlabs/terraform-ecs-fargate-service-discovery/HEAD/env/dev/logs-logzio.zip -------------------------------------------------------------------------------- /env/dev/autoscale-time.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turnerlabs/terraform-ecs-fargate-service-discovery/HEAD/env/dev/autoscale-time.zip -------------------------------------------------------------------------------- /base/service_discovery_zone.tf: -------------------------------------------------------------------------------- 1 | resource "aws_service_discovery_public_dns_namespace" "fargate" { 2 | name = "${var.app}.turnerapps.com" 3 | description = "Fargate discovery managed zone." 4 | } 5 | 6 | output "namespace" { 7 | value = "${aws_service_discovery_public_dns_namespace.fargate.id}" 8 | } 9 | -------------------------------------------------------------------------------- /env/dev/no-alb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group_rule" "nsg_task_ingress_rule_noalb" { 2 | description = "Allow connections to tasks on port ${var.container_port}" 3 | type = "ingress" 4 | from_port = "${var.container_port}" 5 | to_port = "${var.container_port}" 6 | protocol = "TCP" 7 | cidr_blocks = ["0.0.0.0/0"] 8 | 9 | security_group_id = "${aws_security_group.nsg_task.id}" 10 | } 11 | -------------------------------------------------------------------------------- /env/dev/discovery.tf: -------------------------------------------------------------------------------- 1 | resource "aws_service_discovery_service" "fargate" { 2 | name = "${var.environment}" 3 | dns_config { 4 | namespace_id = "${var.service_discovery_namespace}" 5 | routing_policy = "MULTIVALUE" 6 | dns_records { 7 | ttl = 10 8 | type = "A" 9 | } 10 | 11 | dns_records { 12 | ttl = 10 13 | type = "SRV" 14 | } 15 | } 16 | health_check_custom_config { 17 | failure_threshold = 5 18 | } 19 | } 20 | 21 | variable "service_discovery_namespace" {} 22 | 23 | output "discovery_dns" { 24 | value="${var.environment}.${var.app}.turnerapps.com" 25 | } 26 | -------------------------------------------------------------------------------- /base/state.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * state.tf 3 | * Generate a remote state bucket in S3 for use with later Terraform run 4 | * Uses a Turner created Terrafor module; more information at: 5 | * https://github.com/turnerlabs/terraform-remote-state/blob/master/readme.md 6 | * 7 | * To learn more about remote state: 8 | * https://www.terraform.io/docs/state/remote.html 9 | */ 10 | 11 | # s3 bucket for tf remote state 12 | module "tf_remote_state" { 13 | source = "github.com/turnerlabs/terraform-remote-state?ref=v2.0.0" 14 | 15 | role = "${var.saml_role}" 16 | application = "${var.app}" 17 | tags = "${var.tags}" 18 | } 19 | -------------------------------------------------------------------------------- /env/dev/nsg.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "nsg_task" { 2 | name = "${var.app}-${var.environment}-task" 3 | description = "Limit connections from internal resources while allowing ${var.app}-${var.environment}-task to connect to all external resources" 4 | vpc_id = "${var.vpc}" 5 | 6 | tags = "${var.tags}" 7 | } 8 | 9 | resource "aws_security_group_rule" "nsg_task_egress_rule" { 10 | description = "Allows task to establish connections to all resources" 11 | type = "egress" 12 | from_port = "0" 13 | to_port = "0" 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | 17 | security_group_id = "${aws_security_group.nsg_task.id}" 18 | } 19 | -------------------------------------------------------------------------------- /env/dev/fargate-create.yml: -------------------------------------------------------------------------------- 1 | 2 | # this file is used by fargate-create (https://github.com/turnerlabs/fargate-create) 3 | # if not using fargate-create, this file can be ignored/deleted 4 | 5 | prompts: 6 | 7 | - question: "Would you like performance-based auto-scaling?" 8 | default: "yes" 9 | filesToDeleteIfNo: 10 | - "autoscale-perf.tf" 11 | 12 | - question: "Would you like time-based auto-scaling?" 13 | default: "yes" 14 | filesToDeleteIfNo: 15 | - "autoscale-time.tf" 16 | 17 | - question: "Would you like an IAM user that can be used for CI/CD pipelines?" 18 | default: "no" 19 | filesToDeleteIfNo: 20 | - "cicd.tf" 21 | 22 | - question: "Would you like to ship your container logs to logz.io (requires a key)?" 23 | default: "no" 24 | filesToDeleteIfNo: 25 | - "logs-logzio.tf" 26 | - "logs-logzio.zip" 27 | -------------------------------------------------------------------------------- /base/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * main.tf 3 | * The main entry point for Terraform run 4 | * See variables.tf for common variables 5 | * See ecr.tf for creation of Elastic Container Registry for all environments 6 | * See state.tf for creation of S3 bucket for remote state 7 | */ 8 | 9 | # Using the AWS Provider 10 | # https://www.terraform.io/docs/providers/ 11 | provider "aws" { 12 | region = "${var.region}" 13 | profile = "${var.aws_profile}" 14 | } 15 | 16 | /* 17 | * Outputs 18 | * Results from a successful Terraform run (terraform apply) 19 | * To see results after a successful run, use `terraform output [name]` 20 | */ 21 | 22 | # Returns the name of the ECR registry, this will be used later in various scripts 23 | output "docker_registry" { 24 | value = "${aws_ecr_repository.app.repository_url}" 25 | } 26 | 27 | # Returns the name of the S3 bucket that will be used in later Terraform files 28 | output "bucket" { 29 | value = "${module.tf_remote_state.bucket}" 30 | } 31 | -------------------------------------------------------------------------------- /env/dev/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | region = "us-east-1" 4 | profile = "" 5 | bucket = "" 6 | key = "dev.terraform.tfstate" 7 | } 8 | } 9 | 10 | # The AWS Profile to use 11 | variable "aws_profile" {} 12 | 13 | provider "aws" { 14 | region = "${var.region}" 15 | profile = "${var.aws_profile}" 16 | } 17 | 18 | # output 19 | 20 | # Command to view the status of the Fargate service 21 | output "status" { 22 | value = "fargate service info" 23 | } 24 | 25 | # Command to deploy a new task definition to the service using Docker Compose 26 | output "deploy" { 27 | value = "fargate service deploy -f docker-compose.yml" 28 | } 29 | 30 | # Command to scale up cpu and memory 31 | output "scale_up" { 32 | value = "fargate service update -h" 33 | } 34 | 35 | # Command to scale out the number of tasks (container replicas) 36 | output "scale_out" { 37 | value = "fargate service scale -h" 38 | } 39 | 40 | # Command to set the AWS_PROFILE 41 | output "aws_profile" { 42 | value = "${var.aws_profile}" 43 | } 44 | -------------------------------------------------------------------------------- /base/variables.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * variables.tf 3 | * Common variables to use in various Terraform files (*.tf) 4 | */ 5 | 6 | # The AWS region to use for the bucket and registry; typically `us-east-1`. 7 | # Other possible values: `us-east-2`, `us-west-1`, or `us-west-2`. 8 | # Currently, Fargate is only available in `us-east-1`. 9 | variable "region" { 10 | default = "us-east-1" 11 | } 12 | 13 | # The AWS profile to use, this would be the same value used in AWS_PROFILE. 14 | variable "aws_profile" {} 15 | 16 | # The role that will have access to the S3 bucket, this should be a role that all 17 | # members of the team have access to. 18 | variable "saml_role" {} 19 | 20 | # Name of the application. This value should usually match the application tag below. 21 | variable "app" {} 22 | 23 | # A map of the tags to apply to various resources. The required tags are: 24 | # `application`, name of the app; 25 | # `environment`, the environment being created; 26 | # `team`, team responsible for the application; 27 | # `contact-email`, contact email for the _team_; 28 | # and `customer`, who the application was create for. 29 | variable "tags" { 30 | type = "map" 31 | } 32 | -------------------------------------------------------------------------------- /env/dev/variables.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * variables.tf 3 | * Common variables to use in various Terraform files (*.tf) 4 | */ 5 | 6 | # The AWS region to use for the dev environment's infrastructure 7 | # Currently, Fargate is only available in `us-east-1`. 8 | variable "region" { 9 | default = "us-east-1" 10 | } 11 | 12 | # Tags for the infrastructure 13 | variable "tags" { 14 | type = "map" 15 | } 16 | 17 | # The application's name 18 | variable "app" {} 19 | 20 | # The environment that is being built 21 | variable "environment" {} 22 | 23 | # The port the container will listen on, used for load balancer health check 24 | # Best practice is that this value is higher than 1024 so the container processes 25 | # isn't running at root. 26 | variable "container_port" {} 27 | 28 | # The port the load balancer will listen on 29 | variable "lb_port" { 30 | default = "80" 31 | } 32 | 33 | # The load balancer protocol 34 | variable "lb_protocol" { 35 | default = "HTTP" 36 | } 37 | 38 | # Network configuration 39 | 40 | # The VPC to use for the Fargate cluster 41 | variable "vpc" {} 42 | 43 | # The private subnets, minimum of 2, that are a part of the VPC(s) 44 | variable "private_subnets" {} 45 | 46 | # The public subnets, minimum of 2, that are a part of the VPC(s) 47 | variable "public_subnets" {} 48 | -------------------------------------------------------------------------------- /env/dev/role.tf: -------------------------------------------------------------------------------- 1 | # The SAML role to use for adding users to the ECR policy 2 | variable "saml_role" {} 3 | 4 | # creates an application role that the container/task runs as 5 | resource "aws_iam_role" "app_role" { 6 | name = "${var.app}-${var.environment}" 7 | assume_role_policy = "${data.aws_iam_policy_document.app_role_assume_role_policy.json}" 8 | } 9 | 10 | # assigns the app policy 11 | resource "aws_iam_role_policy" "app_policy" { 12 | name = "${var.app}-${var.environment}" 13 | role = "${aws_iam_role.app_role.id}" 14 | policy = "${data.aws_iam_policy_document.app_policy.json}" 15 | } 16 | 17 | # TODO: fill out custom policy 18 | data "aws_iam_policy_document" "app_policy" { 19 | statement { 20 | actions = [ 21 | "ecs:DescribeClusters", 22 | ] 23 | 24 | resources = [ 25 | "${aws_ecs_cluster.app.arn}", 26 | ] 27 | } 28 | } 29 | 30 | data "aws_caller_identity" "current" {} 31 | 32 | # allow role to be assumed by ecs and local saml users (for development) 33 | data "aws_iam_policy_document" "app_role_assume_role_policy" { 34 | statement { 35 | actions = ["sts:AssumeRole"] 36 | 37 | principals { 38 | type = "Service" 39 | identifiers = ["ecs-tasks.amazonaws.com"] 40 | } 41 | 42 | principals { 43 | type = "AWS" 44 | 45 | identifiers = [ 46 | "arn:aws:sts::${data.aws_caller_identity.current.account_id}:assumed-role/${var.saml_role}/me@example.com", 47 | ] 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /base/ecr.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * ecr.tf 3 | * Creates a Amazon Elastic Container Registry (ECR) for the application 4 | * https://aws.amazon.com/ecr/ 5 | */ 6 | 7 | # create an ECR repo at the app/image level 8 | resource "aws_ecr_repository" "app" { 9 | name = "${var.app}" 10 | } 11 | 12 | data "aws_caller_identity" "current" {} 13 | 14 | # grant access to saml users 15 | resource "aws_ecr_repository_policy" "app" { 16 | repository = "${aws_ecr_repository.app.name}" 17 | policy = "${data.aws_iam_policy_document.ecr.json}" 18 | } 19 | 20 | data "aws_iam_policy_document" "ecr" { 21 | statement { 22 | actions = [ 23 | "ecr:GetDownloadUrlForLayer", 24 | "ecr:BatchGetImage", 25 | "ecr:BatchCheckLayerAvailability", 26 | "ecr:PutImage", 27 | "ecr:InitiateLayerUpload", 28 | "ecr:UploadLayerPart", 29 | "ecr:CompleteLayerUpload", 30 | "ecr:DescribeRepositories", 31 | "ecr:GetRepositoryPolicy", 32 | "ecr:ListImages", 33 | "ecr:DescribeImages", 34 | "ecr:DeleteRepository", 35 | "ecr:BatchDeleteImage", 36 | "ecr:SetRepositoryPolicy", 37 | "ecr:DeleteRepositoryPolicy", 38 | "ecr:GetLifecyclePolicy", 39 | "ecr:PutLifecyclePolicy", 40 | "ecr:DeleteLifecyclePolicy", 41 | "ecr:GetLifecyclePolicyPreview", 42 | "ecr:StartLifecyclePolicyPreview", 43 | ] 44 | 45 | principals { 46 | type = "AWS" 47 | 48 | # Add the saml roles for every member on the "team" 49 | identifiers = [ 50 | "arn:aws:sts::${data.aws_caller_identity.current.account_id}:assumed-role/${var.saml_role}/me@example.com", 51 | ] 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /env/dev/cicd.tf: -------------------------------------------------------------------------------- 1 | # create ci/cd user with access keys (for build system) 2 | resource "aws_iam_user" "cicd" { 3 | name = "srv_${var.app}_${var.environment}_cicd" 4 | } 5 | 6 | resource "aws_iam_access_key" "cicd_keys" { 7 | user = "${aws_iam_user.cicd.name}" 8 | } 9 | 10 | # grant required permissions to deploy 11 | data "aws_iam_policy_document" "cicd_policy" { 12 | # allows user to push/pull to the registry 13 | statement { 14 | sid = "ecr" 15 | 16 | actions = [ 17 | "ecr:GetDownloadUrlForLayer", 18 | "ecr:BatchGetImage", 19 | "ecr:BatchCheckLayerAvailability", 20 | "ecr:PutImage", 21 | "ecr:InitiateLayerUpload", 22 | "ecr:UploadLayerPart", 23 | "ecr:CompleteLayerUpload", 24 | ] 25 | 26 | resources = [ 27 | "${data.aws_ecr_repository.ecr.arn}", 28 | ] 29 | } 30 | 31 | # allows user to deploy to ecs 32 | statement { 33 | sid = "ecs" 34 | 35 | actions = [ 36 | "ecr:GetAuthorizationToken", 37 | "ecs:DescribeServices", 38 | "ecs:DescribeTaskDefinition", 39 | "ecs:UpdateService", 40 | "ecs:RegisterTaskDefinition", 41 | ] 42 | 43 | resources = [ 44 | "*", 45 | ] 46 | } 47 | 48 | # allows user to run ecs task using task execution and app roles 49 | statement { 50 | sid = "approle" 51 | 52 | actions = [ 53 | "iam:PassRole", 54 | ] 55 | 56 | resources = [ 57 | "${aws_iam_role.app_role.arn}", 58 | "${aws_iam_role.ecsTaskExecutionRole.arn}", 59 | ] 60 | } 61 | } 62 | 63 | resource "aws_iam_user_policy" "cicd_user_policy" { 64 | name = "${var.app}_${var.environment}_cicd" 65 | user = "${aws_iam_user.cicd.name}" 66 | policy = "${data.aws_iam_policy_document.cicd_policy.json}" 67 | } 68 | 69 | data "aws_ecr_repository" "ecr" { 70 | name = "${var.app}" 71 | } 72 | 73 | # The AWS keys for the CICD user to use in a build system 74 | output "cicd_keys" { 75 | value = "terraform state show aws_iam_access_key.cicd_keys" 76 | } 77 | 78 | # The URL for the docker image repo in ECR 79 | output "docker_registry" { 80 | value = "${data.aws_ecr_repository.ecr.repository_url}" 81 | } -------------------------------------------------------------------------------- /base/README.md: -------------------------------------------------------------------------------- 1 | # Base Terraform 2 | 3 | Creates the foundational infrastructure for the application's infrastructure. 4 | These Terraform files will create a [remote state][state] and a [registry][ecr]. 5 | Most other infrastructure pieces will be created within the `environments` directory. 6 | 7 | 8 | ## Included Files 9 | 10 | + `main.tf` 11 | The main entry point for the Terraform run. 12 | 13 | + `variables.tf` 14 | Common variables to use in various Terraform files. 15 | 16 | + `state.tf` 17 | Generate a [remote state][state] bucket in S3 for use with later Terraform runs. 18 | 19 | + `ecr.tf` 20 | Creates an AWS [Elastic Container Registry (ECR)][ecr] for the application. 21 | 22 | 23 | ## Usage 24 | 25 | Typically, the base Terraform will only need to be run once, and then should only 26 | need changes very infrequently. 27 | 28 | ``` 29 | # Sets up Terraform to run 30 | $ terraform init 31 | 32 | # Executes the Terraform run 33 | $ terraform apply 34 | ``` 35 | 36 | 37 | ## Variables 38 | 39 | | Name | Description | Type | Default | Required | 40 | |------|-------------|:----:|:-----:|:-----:| 41 | | app | Name of the application. This value should usually match the application tag below. | string | | yes | 42 | | aws_profile | The AWS profile to use, this would be the same value used in AWS_PROFILE. | string | | yes | 43 | | region | The AWS region to use for the bucket and registry; typically `us-east-1`. Other possible values: `us-east-2`, `us-west-1`, or `us-west-2`.
Currently, Fargate is only available in `us-east-1`. | string | `us-east-1` | yes | 44 | | saml_role | The role that will have access to the S3 bucket, this should be a role that all members of the team have access to. | string | | yes | 45 | | tags | A map of the tags to apply to various resources. The required tags are:
+ `application`, name of the app
+ `environment`, the environment being created
+ `team`, team responsible for the application
+ `contact-email`, contact email for the _team_
+ `customer`, who the application was create for | map | `` | yes | 46 | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | bucket | Returns the name of the S3 bucket that will be used in later Terraform files | 53 | | docker_registry | Returns the name of the ECR registry, this will be used later in various scripts | 54 | 55 | 56 | ## Additional Information 57 | 58 | + [Terraform remote state][state] 59 | 60 | + [Terraform providers][provider] 61 | 62 | + [AWS ECR][ecr] 63 | 64 | 65 | 66 | [state]: https://www.terraform.io/docs/state/remote.html 67 | [provider]: https://www.terraform.io/docs/providers/ 68 | [ecr]: https://aws.amazon.com/ecr/ 69 | -------------------------------------------------------------------------------- /env/dev/logs-logzio.tf: -------------------------------------------------------------------------------- 1 | # The auth token to use for sending logs to Logz.io 2 | variable "logz_token" {} 3 | 4 | # The endpoint to use for sending logs to Logz.io 5 | variable "logz_url" { 6 | default = "https://listener.logz.io:8071" 7 | } 8 | 9 | resource "aws_iam_role" "iam_for_lambda_logz" { 10 | name = "${var.app}-${var.environment}-logz-role" 11 | 12 | assume_role_policy = <