Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(alb): complete example can be applied without errors #150

Merged
merged 6 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ for example.
| <a name="output_cloudwatch_log_group"></a> [cloudwatch\_log\_group](#output\_cloudwatch\_log\_group) | Name of the CloudWatch log group for container logs. |
| <a name="output_container_definitions"></a> [container\_definitions](#output\_container\_definitions) | Container definitions used by this service including all sidecars. |
| <a name="output_ecr_repository_arn"></a> [ecr\_repository\_arn](#output\_ecr\_repository\_arn) | Full ARN of the ECR repository. |
| <a name="output_ecr_repository_url"></a> [ecr\_repository\_url](#output\_ecr\_repository\_url) | URL of the ECR repository. |
| <a name="output_ecr_repository_id"></a> [ecr\_repository\_id](#output\_ecr\_repository\_id) | The registry ID where the repository was created. |
| <a name="output_ecr_repository_url"></a> [ecr\_repository\_url](#output\_ecr\_repository\_url) | The URL of the repository (in the form `aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName`) |
| <a name="output_task_execution_role_arn"></a> [task\_execution\_role\_arn](#output\_task\_execution\_role\_arn) | ARN of the task execution role that the Amazon ECS container agent and the Docker daemon can assume. |
| <a name="output_task_execution_role_name"></a> [task\_execution\_role\_name](#output\_task\_execution\_role\_name) | Friendly name of the task execution role that the Amazon ECS container agent and the Docker daemon can assume. |
| <a name="output_task_execution_role_unique_id"></a> [task\_execution\_role\_unique\_id](#output\_task\_execution\_role\_unique\_id) | Stable and unique string identifying the IAM role that the Amazon ECS container agent and the Docker daemon can assume. |
Expand Down
18 changes: 8 additions & 10 deletions examples/complete/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,37 @@ Note that this example may create resources which cost money. Run `terraform des

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.9 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.32 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 3.2 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.4 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.9 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.32 |
| <a name="provider_null"></a> [null](#provider\_null) | >= 3.2 |
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.4 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_alb_security_group_public"></a> [alb\_security\_group\_public](#module\_alb\_security\_group\_public) | registry.terraform.io/terraform-aws-modules/security-group/aws | >= 4.17 |
| <a name="module_alb"></a> [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 9.0 |
| <a name="module_service"></a> [service](#module\_service) | ../../ | n/a |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | registry.terraform.io/terraform-aws-modules/vpc/aws | >= 4.0 |
| <a name="module_vpc_endpoints"></a> [vpc\_endpoints](#module\_vpc\_endpoints) | registry.terraform.io/terraform-aws-modules/vpc/aws//modules/vpc-endpoints | >= 4.0 |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |

## Resources

| Name | Type |
|------|------|
| [aws_ecs_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
| [aws_lb.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws_lb_listener.http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_security_group.egress_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [null_resource.initial_image](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/security_group) | data source |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |

## Inputs

Expand All @@ -61,5 +59,5 @@ Note that this example may create resources which cost money. Run `terraform des

| Name | Description |
|------|-------------|
| <a name="output_alb_dns_name"></a> [alb\_dns\_name](#output\_alb\_dns\_name) | n/a |
| <a name="output_endpoint"></a> [endpoint](#output\_endpoint) | n/a |
<!-- END_TF_DOCS -->
7 changes: 2 additions & 5 deletions examples/complete/data.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
data "aws_caller_identity" "current" {}

data "aws_availability_zones" "available" {
state = "available"
}

data "aws_security_group" "default" {
name = "default"
vpc_id = module.vpc.vpc_id
}
155 changes: 72 additions & 83 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
locals {
container_port = 8000
image_tag = "production"

vpc_cidr = "10.0.0.0/16"
azs = slice(data.aws_availability_zones.available.names, 0, 3)
}

resource "random_pet" "this" {
Expand All @@ -12,15 +15,17 @@ resource "aws_ecs_cluster" "this" {
}

module "vpc" {
source = "registry.terraform.io/terraform-aws-modules/vpc/aws"
version = ">= 4.0"
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"

azs = local.azs
cidr = local.vpc_cidr
name = random_pet.this.id
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]

azs = slice(data.aws_availability_zones.available.names, 0, 3)
cidr = "10.0.0.0/16"
enable_dns_hostnames = true
name = random_pet.this.id
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = true

public_subnet_tags = {
Tier = "public"
Expand All @@ -31,95 +36,63 @@ module "vpc" {
}
}

// see https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html for necessary endpoints to run Fargate tasks
module "vpc_endpoints" {
source = "registry.terraform.io/terraform-aws-modules/vpc/aws//modules/vpc-endpoints"
version = ">= 4.0"

security_group_ids = [data.aws_security_group.default.id]
vpc_id = module.vpc.vpc_id

endpoints = {
ecr_api = {
service = "ecr.api"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
}

ecr_dkr = {
service = "ecr.dkr"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
}

logs = {
service = "logs"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
}

s3 = {
service = "s3"
service_type = "Gateway"
route_table_ids = flatten([module.vpc.private_route_table_ids, module.vpc.public_route_table_ids])
}
}
}

module "alb_security_group_public" {
source = "registry.terraform.io/terraform-aws-modules/security-group/aws"
version = ">= 4.17"

name = "fargate-allow-alb-traffic"
use_name_prefix = false
description = "Security group for example usage with ALB"
vpc_id = module.vpc.vpc_id

ingress_cidr_blocks = ["0.0.0.0/0"]
ingress_ipv6_cidr_blocks = ["::/0"]
ingress_rules = ["http-80-tcp"]
egress_rules = ["all-all"]
}
module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "~> 9.0"

#tfsec:ignore:aws-elb-alb-not-public
resource "aws_lb" "public" {
drop_invalid_header_fields = true
enable_deletion_protection = false
load_balancer_type = "application"
name = random_pet.this.id
security_groups = [module.vpc.default_security_group_id, module.alb_security_group_public.security_group_id]
subnets = module.vpc.public_subnets
}

#tfsec:ignore:aws-elb-http-not-used
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.public.arn
port = 80
vpc_id = module.vpc.vpc_id

security_group_ingress_rules = {
all_http = {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
}
}
security_group_egress_rules = {
all = {
ip_protocol = "-1"
cidr_ipv4 = module.vpc.vpc_cidr_block
}
}

default_action {
type = "fixed-response"
listeners = {
http = {
port = 80
protocol = "HTTP"

fixed_response {
content_type = "text/plain"
message_body = "Request was not routed."
status_code = 400
fixed_response = {
content_type = "text/plain"
message_body = "Request was not routed."
status_code = 404
}
}
}
}

module "service" {
source = "../../"
source = "../../"
depends_on = [module.vpc]

cpu = 256
cpu_architecture = "ARM64"
cluster_id = aws_ecs_cluster.this.id
container_port = local.container_port
create_ingress_security_group = false
create_ingress_security_group = true
create_deployment_pipeline = false
desired_count = 1
ecr_force_delete = true
ecr_image_tag = local.image_tag
memory = 512
service_name = random_pet.this.id
security_groups = [aws_security_group.egress_all.id]
vpc_id = module.vpc.vpc_id
ecr_image_tag = local.image_tag

// configure autoscaling for this service
appautoscaling_settings = {
Expand All @@ -136,7 +109,7 @@ module "service" {

// add listener rules that determine how the load balancer routes requests to its registered targets.
https_listener_rules = [{
listener_arn = aws_lb_listener.http.arn
listener_arn = module.alb.listeners["http"].arn

actions = [{
type = "forward"
Expand All @@ -154,7 +127,7 @@ module "service" {
name_prefix = "${substr(random_pet.this.id, 0, 5)}-"
backend_protocol = "HTTP"
backend_port = local.container_port
load_balancer_arn = aws_lb_listener.http.load_balancer_arn
load_balancer_arn = module.alb.arn
target_type = "ip"

health_check = {
Expand All @@ -166,18 +139,34 @@ module "service" {
]
}

resource "null_resource" "initial_image" {
provisioner "local-exec" {
command = "aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${module.service.ecr_repository_url}"
resource "aws_security_group" "egress_all" {
name_prefix = "${random_pet.this.id}-egress-all-"
description = "Allow all outbound traffic"
vpc_id = module.vpc.vpc_id

# make sure to secure traffic in production environments
# see https://avd.aquasec.com/misconfig/aws/ec2/avd-aws-0104/#Terraform
#trivy:ignore:AVD-AWS-0104
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}

lifecycle {
create_before_destroy = true
}
}

resource "null_resource" "initial_image" {
provisioner "local-exec" {
command = "docker build --tag ${module.service.ecr_repository_url}:${local.image_tag} ."
working_dir = "${path.module}/../fixtures/context"
command = "aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com"
}

provisioner "local-exec" {
command = "docker push --all-tags ${module.service.ecr_repository_url}"
command = "docker buildx build --tag ${module.service.ecr_repository_url}:${local.image_tag} --platform linux/amd64,linux/arm64 --push ."
working_dir = "${path.module}/../fixtures/context"
}
}
4 changes: 2 additions & 2 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
output "alb_dns_name" {
value = aws_lb.public.dns_name
output "endpoint" {
value = "http://${module.alb.dns_name}/"
}
4 changes: 2 additions & 2 deletions examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.3"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.9"
version = ">= 5.32"
}
random = {
source = "hashicorp/random"
Expand Down
12 changes: 9 additions & 3 deletions examples/fixtures/context/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
FROM python:3.9-alpine
FROM python:3.12-alpine

ADD index.html index.html
ADD server.py server.py
RUN addgroup -S app && adduser -S app -G app
WORKDIR /home/app

ADD index.html /home/app/index.html
ADD server.py /home/app/server.py

RUN chown -R app:app /home/app

USER app

EXPOSE 8000

ENTRYPOINT ["python3", "server.py"]
11 changes: 6 additions & 5 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
data "aws_lb" "public" {
for_each = var.create_ingress_security_group ? toset([for target in var.target_groups : lookup(target, "load_balancer_arn", "")]) : []
for_each = var.create_ingress_security_group ? { for idx, target in var.target_groups : idx => lookup(target, "load_balancer_arn", "") } : {}
arn = each.value
}

locals {
ingress_targets = flatten(
[
for target in var.target_groups : flatten(
for idx, target in var.target_groups : flatten(
[
[
{
# allow backend_port traffic
from_port = lookup(target, "backend_port", null)
to_port = lookup(target, "backend_port", null)
protocol = "tcp"
source_security_group_id = tolist(data.aws_lb.public[lookup(target, "load_balancer_arn", null)].security_groups)[0]
source_security_group_id = tolist(data.aws_lb.public[idx].security_groups)[0]
prefix = "backend_port"
}
],
Expand All @@ -27,7 +27,7 @@ locals {
from_port = target["health_check"]["port"]
to_port = target["health_check"]["port"]
protocol = "tcp"
source_security_group_id = tolist(data.aws_lb.public[lookup(target, "load_balancer_arn", null)].security_groups)[0]
source_security_group_id = tolist(data.aws_lb.public[idx].security_groups)[0]
prefix = "health_check_port"
}
] : []
Expand Down Expand Up @@ -64,7 +64,8 @@ module "sg" {
}

resource "aws_security_group_rule" "trusted_egress_attachment" {
for_each = { for route in local.ingress_targets : "${route["prefix"]}-${route["source_security_group_id"]}" => route }
depends_on = [data.aws_lb.public]
for_each = { for route in local.ingress_targets : "${route["prefix"]}-${route["protocol"]}-${route["from_port"]}-${route["to_port"]}" => route }
type = "egress"
from_port = each.value["from_port"]
to_port = each.value["to_port"]
Expand Down
3 changes: 2 additions & 1 deletion modules/deployment/iam_code_pipeline.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ data "aws_iam_policy_document" "code_pipepline_permissions" {
resources = [aws_codebuild_project.this.arn]
}

# cloudtrail reports that codepipeline actually requires access to `*`
#trivy:ignore:AVD-AWS-0057
statement {
actions = [
# cloudtrail reports that codepipeline actually requires access to `*`
"ecs:DescribeTaskDefinition",
"ecs:RegisterTaskDefinition",
"ecs:TagResource"
Expand Down
Loading