├── 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 | `