-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e92d618
commit 560bd83
Showing
19 changed files
with
566 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# AWS Fargate Service Module | ||
|
||
This module launches a service on AWS Fargate. It creates a cluster, task | ||
definition, service, and container repository. In addition, it creates the load | ||
balancer, ACM certificate, Route53 records, and security groups needed to expose | ||
the service. | ||
|
||
## Usage | ||
|
||
Add this module to your `main.tf` (or appropriate) file and configure the inputs | ||
to match your desired configuration. For example: | ||
|
||
```hcl | ||
module "cloudfront_waf" { | ||
source = "github.com/codeforamerica/tofu-modules/aws/fargate_service" | ||
project = "my-project" | ||
environment = "dev" | ||
} | ||
``` | ||
|
||
Make sure you re-run `tofu init` after adding the module to your configuration. | ||
|
||
```bash | ||
tofu init | ||
tofu plan | ||
``` | ||
|
||
To update the source for this module, pass `-upgrade` to `tofu init`: | ||
|
||
```bash | ||
tofu init -upgrade | ||
``` | ||
|
||
## Inputs | ||
|
||
| Name | Description | Type | Default | Required | | ||
|--------------------------|------------------------------------------------------------------------------------|----------|------------|----------| | ||
| domain | Domain name for service. Example: `"staging.service.org"` | `string` | n/a | yes | | ||
| logging_key_id | KMS key to use for log encryption. | `string` | n/a | yes | | ||
| private_subnets | List of private subnet CIDR blocks. | `list` | n/a | yes | | ||
| project | Name of the project. | `string` | n/a | yes | | ||
| project_short | Short name for the project. Used in resource names with character limits. | `string` | n/a | yes | | ||
| public_subnets | List of public subnet CIDR blocks. | `list` | n/a | yes | | ||
| service | Service that these resources are supporting. Example: `"api"`, `"web"`, `"worker"` | `string` | n/a | yes | | ||
| service_short | Short name for the service. Used in resource names with character limits. | `string` | n/a | yes | | ||
| vpc_id | Id of the VPC to deploy into. | `string` | n/a | yes | | ||
| container_port | Port the container listens on. | `number` | `80` | no | | ||
| environment | Environment for the project. | `string` | `"dev"` | no | | ||
| image_tag | Tag of the container image to be deployed. | `string` | `"latest"` | no | | ||
| internal | Creates an internal ALB instead of a public one. | `bool` | `false` | no | | ||
| key_recovery_period | Number of days to recover the service KMS key after deletion. | `number` | `30` | no | | ||
| log_retention_period | Retention period for flow logs, in days. | `number` | `30` | no | | ||
| untagged_image_retention | Retention period (after push) for untagged images, in days. | `number` | `14` | no | | ||
|
||
## Outputs | ||
|
||
| Name | Description | Type | | ||
|--------------|--------------------------------------------------------------|----------| | ||
| cluster_name | Name of the ECS Fargate cluster. | `string` | | ||
| docker_push | Commands to push a Docker image to the container repository. | `string` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
module "alb" { | ||
source = "terraform-aws-modules/alb/aws" | ||
version = "~> 9.9" | ||
enable_deletion_protection = false | ||
|
||
name = local.prefix_short | ||
load_balancer_type = "application" | ||
security_groups = [module.endpoint_security_group.security_group_id] | ||
subnets = var.internal ? var.private_subnets : var.public_subnets | ||
vpc_id = var.vpc_id | ||
|
||
# TODO: Support IPv6 and/or dualstack. | ||
ip_address_type = "ipv4" | ||
|
||
listeners = { | ||
http = { | ||
port = 80 | ||
protocol = "HTTP" | ||
redirect = { | ||
port = "443" | ||
protocol = "HTTPS" | ||
status_code = "HTTP_301" | ||
} | ||
} | ||
|
||
https = { | ||
port = 443 | ||
protocol = "HTTPS" | ||
ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" | ||
certificate_arn = aws_acm_certificate.endpoint.arn | ||
forward = { | ||
target_group_key = "endpoint" | ||
} | ||
} | ||
} | ||
|
||
target_groups = { | ||
endpoint = { | ||
name = "${local.prefix_short}-app" | ||
protocol = "HTTP" | ||
target_type = "ip" | ||
port = var.container_port | ||
|
||
# Theres nothing to attach here in this definition. Instead, ECS will | ||
# attach the IPs of the tasks to this target group. | ||
create_attachment = false | ||
|
||
health_check = { | ||
path = "/health" | ||
healthy_threshold = 5 | ||
unhealthy_threshold = 2 | ||
} | ||
} | ||
} | ||
} | ||
|
||
resource "aws_acm_certificate" "endpoint" { | ||
domain_name = var.domain | ||
validation_method = "DNS" | ||
|
||
lifecycle { | ||
create_before_destroy = true | ||
} | ||
} | ||
|
||
resource "aws_route53_record" "endpoint" { | ||
name = var.domain | ||
type = "A" | ||
zone_id = data.aws_route53_zone.domain.zone_id | ||
|
||
alias { | ||
name = module.alb.dns_name | ||
zone_id = module.alb.zone_id | ||
evaluate_target_health = true | ||
} | ||
} | ||
|
||
resource "aws_route53_record" "endpoint_validation" { | ||
for_each = { | ||
for dvo in aws_acm_certificate.endpoint.domain_validation_options : dvo.domain_name => { | ||
name = dvo.resource_record_name | ||
record = dvo.resource_record_value | ||
type = dvo.resource_record_type | ||
} | ||
} | ||
|
||
allow_overwrite = true | ||
name = each.value.name | ||
records = [each.value.record] | ||
ttl = 60 | ||
type = each.value.type | ||
zone_id = data.aws_route53_zone.domain.zone_id | ||
} | ||
|
||
resource "aws_acm_certificate_validation" "endpoint" { | ||
certificate_arn = aws_acm_certificate.endpoint.arn | ||
validation_record_fqdns = [ | ||
for record in aws_route53_record.endpoint_validation : record.fqdn | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
data "aws_caller_identity" "identity" {} | ||
|
||
data "aws_partition" "current" {} | ||
|
||
data "aws_region" "current" {} | ||
|
||
data "aws_route53_zone" "domain" { | ||
name = var.domain | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
resource "aws_iam_policy" "execution" { | ||
name = "${local.prefix}-execution" | ||
description = "${var.service} task execution policy for ${var.project} ${var.environment}." | ||
|
||
policy = jsonencode(yamldecode(templatefile("${path.module}/templates/execution-policy.yaml.tftpl", { | ||
project = var.project | ||
environment = var.environment | ||
ecr_arn = module.ecr.repository_arn | ||
}))) | ||
} | ||
|
||
resource "aws_iam_role" "execution" { | ||
name = "${local.prefix}-execution" | ||
description = "${var.service} task execution role for ${var.project} ${var.environment}." | ||
|
||
assume_role_policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [ | ||
{ | ||
Effect = "Allow" | ||
Action = "sts:AssumeRole" | ||
Principal = { | ||
Service = "ecs-tasks.amazonaws.com" | ||
} | ||
} | ||
] | ||
}) | ||
|
||
managed_policy_arns = [ | ||
# aws_iam_policy.execution.arn | ||
"arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", | ||
] | ||
} | ||
|
||
resource "aws_iam_role" "task" { | ||
name = "${local.prefix}-task" | ||
description = "${var.service} task role for ${var.project} ${var.environment}." | ||
|
||
assume_role_policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [ | ||
{ | ||
Effect = "Allow" | ||
Action = "sts:AssumeRole" | ||
Principal = { | ||
Service = "ecs-tasks.amazonaws.com" | ||
} | ||
} | ||
] | ||
}) | ||
|
||
managed_policy_arns = [ | ||
"arn:${data.aws_partition.current.partition}:iam::aws:policy/CloudWatchFullAccess", | ||
"arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonSSMFullAccess" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
resource "aws_kms_key" "fargate" { | ||
description = "${var.service} hosting encryption key for ${var.project} ${var.environment}" | ||
deletion_window_in_days = var.key_recovery_period | ||
enable_key_rotation = true | ||
policy = jsonencode(yamldecode(templatefile("${path.module}/templates/key-policy.yaml.tftpl", { | ||
account_id : data.aws_caller_identity.identity.account_id, | ||
partition : data.aws_partition.current.partition, | ||
region : data.aws_region.current.name, | ||
repository_name : local.prefix, | ||
}))) | ||
} | ||
|
||
resource "aws_kms_alias" "fargate" { | ||
name = "alias/${var.project}/${var.environment}/${var.service}" | ||
target_key_id = aws_kms_key.fargate.id | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
locals { | ||
prefix = "${var.project}-${var.environment}-${var.service}" | ||
prefix_short = "${var.project_short}-${var.environment}-${var.service_short}" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
resource "aws_cloudwatch_log_group" "service" { | ||
name = "/aws/ecs/${var.project}/${var.environment}/${var.service}" | ||
retention_in_days = 30 | ||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
module "ecr" { | ||
source = "terraform-aws-modules/ecr/aws" | ||
version = "~> 2.2" | ||
|
||
repository_name = local.prefix | ||
repository_image_scan_on_push = true | ||
repository_encryption_type = "KMS" | ||
repository_kms_key = aws_kms_key.fargate.arn | ||
repository_lifecycle_policy = jsonencode(yamldecode(templatefile( | ||
"${path.module}/templates/repository-lifecycle.yaml.tftpl", { | ||
untagged_image_retention : var.untagged_image_retention | ||
} | ||
))) | ||
} | ||
|
||
# TODO: Configure internal CIDR | ||
module "endpoint_security_group" { | ||
source = "terraform-aws-modules/security-group/aws" | ||
version = "~> 5.1" | ||
|
||
name = "${local.prefix}-endpoint" | ||
vpc_id = var.vpc_id | ||
|
||
# Ingress for HTTP | ||
ingress_cidr_blocks = [var.internal ? "10.0.0.0/16" : "0.0.0.0/0"] | ||
ingress_rules = ["http-80-tcp", "https-443-tcp"] | ||
|
||
# Allow all egress | ||
egress_cidr_blocks = ["0.0.0.0/0"] | ||
egress_ipv6_cidr_blocks = ["::/0"] | ||
egress_rules = ["all-all"] | ||
} | ||
|
||
# TODO: Configure internal CIDR | ||
module "task_security_group" { | ||
source = "terraform-aws-modules/security-group/aws" | ||
version = "~> 5.1" | ||
|
||
name = "${local.prefix}-endpoint" | ||
vpc_id = var.vpc_id | ||
|
||
ingress_with_source_security_group_id = [{ | ||
from_port = var.container_port | ||
to_port = var.container_port | ||
protocol = "tcp" | ||
description = "${var.service} access from the load balancer." | ||
source_security_group_id = module.endpoint_security_group.security_group_id | ||
}] | ||
|
||
# Allow all egress | ||
# TODO: Can we restrict this? | ||
egress_cidr_blocks = ["0.0.0.0/0"] | ||
egress_ipv6_cidr_blocks = ["::/0"] | ||
egress_rules = ["all-all"] | ||
} | ||
|
||
module "ecs" { | ||
source = "HENNGE/ecs/aws" | ||
version = "~> 4.2" | ||
|
||
name = local.prefix | ||
capacity_providers = ["FARGATE"] | ||
enable_container_insights = true | ||
} | ||
|
||
module "ecs_service" { | ||
source = "HENNGE/ecs/aws//modules/simple/fargate" | ||
version = "~> 4.2" | ||
depends_on = [module.alb, module.ecs] | ||
|
||
name = local.prefix | ||
cluster = module.ecs.arn | ||
container_port = var.container_port | ||
container_name = local.prefix | ||
cpu = 256 | ||
memory = 512 | ||
desired_count = 1 | ||
vpc_subnets = var.private_subnets | ||
target_group_arn = module.alb.target_groups["endpoint"].arn | ||
security_groups = [module.task_security_group.security_group_id] | ||
iam_daemon_role = aws_iam_role.execution.arn | ||
iam_task_role = aws_iam_role.task.arn | ||
|
||
container_definitions = jsonencode(yamldecode(templatefile( | ||
"${path.module}/templates/container_definitions.yaml.tftpl", { | ||
name = local.prefix | ||
cpu = 256 | ||
memory = 512 | ||
image = "${module.ecr.repository_url}:${var.image_tag}" | ||
container_port = "${var.container_port}" | ||
log_group = aws_cloudwatch_log_group.service.name | ||
region = data.aws_region.current.name | ||
} | ||
))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
output "cluster_name" { | ||
value = module.ecs.name | ||
} | ||
|
||
output "docker_push" { | ||
value = <<EOT | ||
aws ecr get-login-password --region ${data.aws_region.current.name} | docker login --username AWS --password-stdin ${module.ecr.repository_url} | ||
docker build -t ${module.ecr.repository_name} . | ||
docker tag ${module.ecr.repository_name}:${var.image_tag} ${module.ecr.repository_url}:latest | ||
docker push ${module.ecr.repository_url}:latest | ||
EOT | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
provider "aws" { | ||
default_tags { | ||
tags = { | ||
service = var.service | ||
} | ||
} | ||
} |
Oops, something went wrong.