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.
Add this module to your main.tf (or appropriate) file and configure the inputs
to match your desired configuration. For example:
module "fargate_service" {
source = "github.com/codeforamerica/tofu-modules-aws-fargate-service?ref=1.7.0"
project = "my-project"
project_short = "my-proj"
environment = "dev"
service = "worker"
service_short = "wrk"
domain = "dev.worker.my-project.org"
vpc_id = module.vpc.vpc_id
private_subnets = module.vpc.private_subnets
public_subnets = module.vpc.public_subnets
logging_key_id = module.logging.kms_key_arn
container_port = 3000
environment_variables = {
RACK_ENV = "development"
}
}Make sure you re-run tofu init after adding the module to your configuration.
tofu init
tofu planTo update the source for this module, pass -upgrade to tofu init:
tofu init -upgradeWhen you use OpenTofu to manage your services and tasks, it can become out of sync if you're deploying new task versions outside of it. To facilitate deployment of new container images (e.g., application versions, patches, etc.) this module offers three ways to define the current image version:
-
(Recommended) Use
create_version_parameterto create and manage an (insecure) SSM parameter. The initial value will be set toimage_tag(default "latest"), but updates to the value must occur outside OpenTofu. The current value of this parameter will be read when runningtofu.Use your normal CI/CD deployment process to build and push your image. Then set the new version tag into the parameter before running
tofu apply. This allows you to completely automate the deployment process if you wish. -
Use
version_parameterto specify your own SSM parameter. The task must be able to retrieve the value of the parameter. This method allows for the same automated deployment process as the previous one, but requires you to create and maintain the parameter outside this module. -
(Default) Use
image_tagto specify the image tag to deploy. This is the default behavior and will be used if neither of the other inputs has been set. This requires theimage_taginput variable to be updated anytime the desired version changes.When using
create_version_parameter,image_tagwill be used to set the initial value, but future updates to this variable will be ignored. This allows for easier initial deployment and migration to using the SSM parameter.
This module supports an optional AWS AppConfig Agent sidecar
container. The agent runs alongside your application, caching configuration
locally and serving it over HTTP on localhost. This enables live configuration
updates without redeploying your service.
To enable the agent, set enable_appconfig_agent to true:
enable_appconfig_agent = true
appconfig_agent_environment_variables = {
PREFETCH_LIST = "/applications/example-application/environments/dev/configurations/feature-flags"
}The agent requires the appconfig:StartConfigurationSession and
appconfig:GetLatestConfiguration IAM permissions. These are automatically
added to the task role when the agent is enabled.
Your application retrieves configuration by making HTTP GET requests to
http://localhost:2772/applications/{app}/environments/{env}/configurations/{profile}.
The port can be customized with appconfig_agent_port.
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| enable_appconfig_agent | Enable the AWS AppConfig Agent sidecar container. | bool |
false |
no |
| appconfig_agent_application_id | AppConfig application ID to scope IAM permissions. | string |
"" |
conditional |
| appconfig_agent_environment_variables | Environment variables for the AppConfig Agent sidecar. | map(string) |
{} |
no |
| appconfig_agent_port | Port for the AppConfig Agent HTTP server. | number |
2772 |
no |
| appconfig_agent_version | Version of the AWS AppConfig Agent image. Pin for production stability. | string |
"2.x" |
no |
The container definition produced by this module includes a sidecar of the AWS Distro for OpenTelemetry (ADOT) Collector. This can be used to collect metrics, logs, and traces from your application. This greatly enhances the overall observability of your application.
The latest version is installed by default. It's recommend that you pin this to
a specific version using otel_collector_version and perform regular updates on
your own schedule.
Tip
To take full advantage of this functionally, make sure your application is instrumented using OpenTelemetry (OTEL).
By default, the collector is configured to send all supported instrumentation to the appropriate AWS services:
- Metrics are sent to CloudWatch
- Logs are sent to CloudWatch Logs
- Traces are sent to X-Ray
With the default configuration, you can set stats_prefix to a custom value to
prefix all metrics with. By default, this is set to
"${var.project}/${var.service}".
You can override this by setting a custom configuration with otel_config. To
help facilitate additional keys or other secrets that may be necessary, you can
set otel_secrets to a map of secrets from AWS Secrets Manager or SSM Parameter
Store.
For example:
otel_collector_version = "v0.47.0"
otel_config = file("${path.module}/files/aws-otel-config.yaml")
otel_secrets = {
DD_API_KEY = ""arn:aws:secretsmanager:us-east-1:123456789012:secret:project/staging/DD_API_KEY"
}Additionally, to help debug OTEL related issues, you can override the log level
by setting otel_log_level to a valid java.util.logging log
level.
The cpu and memory variables define the total resources for the entire
Fargate task, not just your application container. All containers in the task
share these resources dynamically.
The OpenTelemetry collector sidecar is always included and reserves a fixed portion of the task resources:
| Container | CPU (units) | Memory (MiB) |
|---|---|---|
| OTEL Collector | 256 | 512 |
| Your application | Remainder | Remainder |
The AppConfig Agent sidecar does not reserve dedicated CPU or memory. When enabled, it shares the task's remaining resources with your application. The agent is lightweight and consumes minimal resources under normal operation.
With the defaults of cpu = 512 and memory = 1024, your application
container receives 256 CPU units and 512 MiB of memory, regardless of whether
the AppConfig Agent is enabled.
Important
If your application needs more resources, increase the task-level cpu and
memory values. Fargate task sizes must conform to supported CPU and memory
values.
Caution
The use_target_group_port_suffix option will default to true in the next
major release. If you are using this module and do not want to use the
listener port as a suffix for the target group, you should set this to false
now to avoid unexpected changes in the future.
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| 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 |
| 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 |
| public_subnets | List of public subnet CIDR blocks. Required when creating a public endpoint. | list |
n/a | conditional |
| domain | Domain name for service. Required if creating an endpoint. Example: "staging.service.org" |
string |
"" |
conditional |
| image_url | URL of the container image. Required if not creating a repository. | string |
"" |
conditional |
| appconfig_agent_application_id | AppConfig application ID to scope IAM permissions. Required when enable_appconfig_agent is true. |
string |
"" |
conditional |
| appconfig_agent_environment_variables | Environment variables for the AppConfig Agent sidecar. Only used when enable_appconfig_agent is true. |
map(string) |
{} |
no |
| appconfig_agent_port | Port for the AppConfig Agent HTTP server. Only used when enable_appconfig_agent is true. |
number |
2772 |
no |
| appconfig_agent_version | Version of the AWS AppConfig Agent image. Pin to a specific version for production stability. | string |
"2.x" |
no |
| container_command | Command to run in the container. Defaults to the image's CMD. | list(string) |
[] |
no |
| container_port | Port the container listens on. | number |
80 |
no |
| create_endpoint | Create an Application Load Balancer for the service. Required to serve traffic. | bool |
true |
no |
| create_repository | Create an ECR repository to host the container image. | bool |
true |
no |
| create_version_parameter | Create an SSM parameter to store the active version for the image tag. | bool |
false |
no |
| cpu | CPU unit for this task. | number |
512 |
no |
| desired_containers | Desired number of running containers for the service. | number |
1 |
no |
| enable_appconfig_agent | Enable an AWS AppConfig Agent sidecar container for live configuration updates. | bool |
false |
no |
| enable_circuit_breaker | Enable ECS deployment circuit breaker to detect failed deployments. | bool |
false |
no |
| enable_circuit_breaker_rollback | Enable rollback of the service when the circuit breaker is triggered. This will roll back the service to the previous version. Only used if enable_circuit_breaker is true. |
bool |
false |
no |
| enable_container_insights_enhanced | Enable enhanced container insights for the service. | bool |
true |
no |
| enable_execute_command | Enable the ECS ExecuteCommand feature. | bool |
false |
no |
| environment | Environment for the project. | string |
"dev" |
no |
| environment_secrets | Secrets to be injected as environment variables into the container. | map(string) |
{} |
no |
| environment_variables | Environment variables to be set on the container. | map(string) |
{} |
no |
| execution_policies | Additional policies for the task execution role. | list(string) |
[] |
no |
| force_delete | Force deletion of resources. If changing to true, be sure to apply before destroying. | bool |
false |
no |
| force_new_deployment | Force a new task deployment of the service. | bool |
false |
no |
| health_check_grace_period | Time, in seconds, after a container comes into service before health checks must pass. | number |
300 |
no |
| health_check_path | Application path to use for health checks. | string |
"/health" |
no |
| hosted_zone_id | ID of the hosted zone for the domain, leave empty to have this module look it up. | string |
null |
no |
| image_tag | Tag of the container image to be deployed. | string |
"latest" |
no |
| image_tags_mutable | Whether the container repository allows tags to be mutated. | bool |
false |
no |
| ingress_cidrs | List of additional CIDR blocks to allow traffic from. | list |
[] |
no |
| ingress_prefix_list_ids | List of prefix list IDs to allow ingress from. | list |
[] |
no |
| key_recovery_period | Recovery period for deleted KMS keys in days. Must be between 7 and 30. |
number |
30 |
no |
| logging_bucket | S3 bucket to use for logging. If not provided, load balancer logs will not be created. | string |
null |
no |
| logging_bucket_prefix | Prefix for the S3 bucket to use for logging. | string |
null |
no |
| log_retention_period | Retention period for CloudWatch Logs, in days. | number |
30 |
no |
| manage_performance_log_group | Whether to manage the container insights performance log group for the service. Will default to true in a future release. |
bool |
false |
no |
| memory | Memory for this task. | number |
1024 |
no |
| oidc_settings | OIDC connection settings for the service endpoint. | object |
null |
no |
| otel_config | Custom configuration, in YAML format, for the OpenTelemetry collector. If left empty, a default configuration will be used that sends all metrics, logs, and traces to the appropriate AWS services. | string |
null |
no |
| otel_log_level | Log level for the OpenTelemetry collector. | string |
"info" |
no |
| otel_secrets | Secrets to be injected as environment variables into the OpenTelemetry collector container. This is primarily used alongside a custom otel_config. Should be in the same format as environment_secrets. |
map(string) |
{} |
no |
| otel_collector_version | Version of the AWS Distro for OpenTelemetry (ADOT) Collector to use. Defaults to latest, but it's recommended to pin this to a specific version. |
string |
"latest" |
no |
| public | Whether the service should be exposed to the public Internet. | bool |
false |
no |
| repository_arn | ARN of the ECR repository hosting the image. Only required if using a private repository, but not created here. | string |
"" |
no |
| secrets_manager_secrets | Map of secrets to be created in Secrets Manager. | map(object) |
{} |
no |
| stats_prefix | Prefix for statsd metrics. Defaults to project/service. |
string |
"" |
no |
| subdomain | Optional subdomain for the service, to be appended to the domain for DNS. | string |
"" |
no |
| tags | Optional tags to be applied to all resources. | list |
[] |
no |
| task_policies | Additional policies for the task role. | list(string) |
[] |
no |
| untagged_image_retention | Retention period (after push) for untagged images, in days. | number |
14 |
no |
| use_target_group_port_suffix | Whether to use the listener port as a suffix for the ALB listener rule. Useful if you way need to replace the target group at some point. | bool |
false |
no |
| version_parameter | Optional SSM parameter to use for the image tag. | string |
null |
no |
| volumes | Volumes to mount in the container. | map(object) |
{} |
no |
| wait_for_steady_state | Whether to wait for the service to reach a steady state before considering the deployment successful. It's highly recommend that you set enable_circuit_breaker to true when using this option to avoid OpenTofu from timing out. |
bool |
false |
no |
In order to override the command that the container runs, you can provide a list of strings to be passed to the container. For example, to run a container that starts rack, you could use:
container_command = ["bundle", "exec", "rackup"]An optional map of secrets to be injected as environment variables into the
container. The key is the name of the environment variable, and the value is the
identifier and key of a secret, separated by :. The identifier may be the name
of a secret defined using secrets_manager_secrets, or the full ARN of an
existing secret. For example:
environment_secrets = {
EXAMPLE_CLIENT_ID = "client:client_id"
EXAMPLE_CLIENT_KEY = "arn:aws:secretsmanager:us-east-1:123456789012:secret:project/staging/client:key"
}Warning
The next major release (2.0.0) of this module will default this option to
true and mark it as deprecated. We recommend setting this to true for new
deployments, and following the steps below to update existing deployments.
By default, this module only creates and manages the task log group, found at
/aws/ecs/#{var.project}/${var.environment}/${var.service}. However, AWS also
creates a performance log group for Container Insights at
/aws/containerinsights/${var.project}-${var.environment}-${var.service}/performance.
This can lead to a mismatch between the configuration of your log groups.
The manage_performance_log_group option allows you to have this module manage
the performance log group as well. This will ensure that the retention period,
encryption settings, and tags are consistent. However, enabling this option for
an existing deployment may lead to resource conflicts, as the log group already
exists.
To migrate an existing deployment, follow these steps to import the existing log group.
-
Upgrade this module to the latest version
-
Set
manage_performance_log_grouptotruein your configuration -
Identify the location of this module in your configuration (e.g.:
module.fargate_service) -
Run the following command, or use an
importblock, to import the existing log group, replacing the module path and log group name needed:tofu import \ 'module.fargate_service.aws_cloudwatch_log_group.this["performance"]' \ /aws/containerinsights/my-project-dev-worker/performance -
Apply your updated configuration:
tofu apply
If you want to authenticate users to your service before they can access it, you can configure an OpenID Connect (OIDC) provider. Configure the connection on your OIDC provider (e.g., Okta, Auth0, etc.) and then provide the settings here.
Caution
The client_secret is a sensitive value and should not be stored in
version control. It is recommended to store it in AWS Secrets Manager and
provide the ARN of the secret using client_secret_arn.
The provided secret must contain the client_id and client_secret keys.
oidc_settings = {
client_secret_arn = module.secrets.secrets["oidc"].secret_arn
authorization_endpoint = "https://myorg.okta.com/oauth2/v1/authorize"
issuer = "https://myorg.okta.com"
token_endpoint = "https://myorg.okta.com/oauth2/v1/token"
user_info_endpoint = "https://myorg.okta.com/oauth2/v1/userinfo"
}| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| authorization_endpoint | Authorization endpoint from your provider. | string |
n/a | yes |
| issuer | Issuer endpoint from your provider. | string |
n/a | yes |
| token_endpoint | Token endpoint from your provider. | string |
n/a | yes |
| user_info_endpoint | User info endpoint from your provider. | string |
n/a | yes |
| client_id | Client ID from your provider. | string |
"" |
conditional |
| client_secret | Client secret from your provider. | string |
"" |
conditional |
| client_secret_arn | Secrets manager ARN where the client id and secret can be found. | string |
"" |
conditional |
Caution
This feature may be removed in a future version. It is recommended to use the secrets module to manage secrets instead.
An optional map of secrets to be created in AWS Secrets
Manager. Once the secret is created, any changes to the value
will be ignored. For example, to create a secret named example:
secrets_manager_secrets = {
example = {
recovery_window = 7
description = "Example credentials for our application."
}
}| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| description | Description of the secret. | string |
n/a | yes |
| recovery_window | Number of days that a secret can be recovered after deltion. | string |
30 |
no |
| create_random_password | Creates a random password as the staring value. | bool |
false |
no |
| start_value | Value to be set into the secret at creation. | string |
"{}" |
no |
You can use the volumes input to mount volumes in the container. Currently,
only persistent volumes are supported. For each volume, an EFS file system
will be created and mounted in the container.
volumes = {
data = {
type = "persistent"
mount = "/data"
}
}| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| mount | Path in the container where the volume will be mounted. | string |
n/a | yes |
| name | Name of the volume. Defauls to the key from the map. | string |
null |
no |
| type | Type of volume to create. Currently only persistent is supported. |
string |
"persistent" |
no |
| 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 |
| endpoint_security_group_id | Security group ID for the endpoint. | string |
| endpoint_url | URL of the service endpoint. | string |
| execution_role_arn | ARN of the role used to execute tasks. | string |
| load_balancer_arn | ARN of the load balancer, if created. | string |
| log_group_names | Names of managed CloudWatch log groups for the service. | list(string) |
| repository_arn | ARN of the ECR repository, if created. | string |
| repository_url | URL for the container image repository. | string |
| security_group_id | Security group ID for the service. | string |
| task_role_arn | ARN of the role attached to the running tasks. | string |
| version_parameter | Name of the SSM parameter, if one exists, to store the current version. | string |