Skip to main content
The AWS ECS Terraform module creates up to five distinct IAM roles depending on which features you enable. Each role has a specific purpose tied to a different phase of the ECS task lifecycle or a different compute type.

Role summary

RolePrincipalCreated byPurpose
Task execution roleecs-tasks.amazonaws.comCluster or service sub-moduleLets ECS pull images and retrieve secrets during task launch
Tasks IAM roleecs-tasks.amazonaws.comService sub-moduleLets containers access AWS services at runtime
Service IAM roleecs.amazonaws.comService sub-moduleLets ECS register/deregister load balancer targets
Infrastructure IAM roleecs.amazonaws.comCluster sub-moduleUsed by ECS to manage EC2 fleet for Managed Instances
Node IAM roleec2.amazonaws.comCluster sub-moduleUsed by EC2 nodes in the Managed Instances fleet

Task execution role

The task execution role is assumed by the ECS service (ecs-tasks.amazonaws.com) during task launch — not by the containers themselves. ECS uses this role to:
  • Pull container images from Amazon ECR.
  • Write log streams to CloudWatch Logs.
  • Retrieve SSM Parameter Store values injected as environment variables.
  • Retrieve Secrets Manager secrets injected as environment variables.
The task execution role’s permissions are not accessible to the containers at runtime. They are consumed only during the task creation process. Runtime access to AWS services is handled by the tasks IAM role.

Shared vs. per-service patterns

Create one task execution role in the cluster sub-module and share it across all services in the cluster. This is the simplest pattern and works well when all services in the cluster are owned by the same team.
module "cluster" {
  source  = "terraform-aws-modules/ecs/aws//modules/cluster"
  version = "~> 7.0"

  name = "my-cluster"

  # Create the role here — all services will inherit it
  create_task_exec_iam_role = true
  create_task_exec_policy   = true

  # Optionally scope SSM and Secrets Manager access at the cluster level
  task_exec_ssm_param_arns = ["arn:aws:ssm:us-east-1:123456789012:parameter/shared/*"]
  task_exec_secret_arns    = ["arn:aws:secretsmanager:us-east-1:123456789012:secret:shared/*"]
}

module "service" {
  source  = "terraform-aws-modules/ecs/aws//modules/service"
  version = "~> 7.0"

  name        = "api"
  cluster_arn = module.cluster.arn

  # Don't create a new role — use the one from the cluster module
  create_task_exec_iam_role = false
  task_exec_iam_role_arn    = module.cluster.task_exec_iam_role_arn
}
When using the root module, this is handled automatically: setting create_task_exec_iam_role = true at the top level creates the cluster-level role, and its ARN is forwarded to every service via task_exec_iam_role_arn = try(coalesce(each.value.task_exec_iam_role_arn, module.cluster.task_exec_iam_role_arn), null).
The module creates a custom IAM policy (rather than attaching the AWS-managed AmazonECSTaskExecutionRolePolicy) so that permissions can be scoped precisely. The policy includes:
StatementActionsResources
Logslogs:CreateLogStream, logs:PutLogEvents*
ECRecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:GetDownloadUrlForLayer, ecr:BatchGetImage*
GetSSMParamsssm:GetParametersARNs from task_exec_ssm_param_arns
GetSecretssecretsmanager:GetSecretValueARNs from task_exec_secret_arns
CustomAnyFully configurable via task_exec_iam_statements
Additional managed policies can be attached via task_exec_iam_role_policies (a map of policy name to ARN).The GetSSMParams and GetSecrets statements are only included when the respective ARN lists are non-empty, keeping the policy minimal by default.

Controlling variables

VariableModuleDefaultDescription
create_task_exec_iam_roleCluster, ServicetrueWhether to create the role
create_task_exec_policyCluster, ServicetrueWhether to create and attach the inline policy
task_exec_iam_role_nameCluster, Service<cluster/service>-task-execRole name
task_exec_iam_role_use_name_prefixCluster, ServicetrueUse a name prefix instead of a fixed name
task_exec_iam_role_policiesCluster, Service{}Additional managed policies to attach
task_exec_ssm_param_arnsCluster, Service[]SSM parameter ARNs to allow ssm:GetParameters
task_exec_secret_arnsCluster, Service[]Secrets Manager ARNs to allow secretsmanager:GetSecretValue
task_exec_iam_statementsCluster, Service{}Additional inline policy statements
task_exec_iam_role_permissions_boundaryCluster, ServicenullPermissions boundary ARN

Tasks IAM role

The tasks IAM role is assumed by the containers within the task at runtime. This is analogous to an EC2 instance profile or a Kubernetes IRSA role. If your application needs to read from S3, connect to RDS using IAM authentication, publish to SQS, or call any AWS API, those permissions belong on the tasks IAM role. The tasks IAM role is created by the service sub-module only. It is scoped to the specific service.
The base tasks IAM role has no permissions by default. Permissions are added through:
MechanismVariableDescription
Inline policy statementstasks_iam_role_statementsList of IAM policy statement objects
Managed policy attachmentstasks_iam_role_policiesMap of policy name to ARN
ECS Execenable_execute_commandAutomatically adds ssmmessages:* permissions required for ecs execute-command
When enable_execute_command = true, the module injects the following statement:
statement {
  sid = "ECSExec"
  actions = [
    "ssmmessages:CreateControlChannel",
    "ssmmessages:CreateDataChannel",
    "ssmmessages:OpenControlChannel",
    "ssmmessages:OpenDataChannel",
  ]
  resources = ["*"]
}
The assume role policy includes two conditions to prevent confused deputy attacks:
  • aws:SourceArn must match arn:*:ecs:<region>:<account>:*
  • aws:SourceAccount must match the deploying account ID

Controlling variables

VariableDefaultDescription
create_tasks_iam_roletrueWhether to create the tasks IAM role
tasks_iam_role_arnnullARN of a pre-existing role to use instead
tasks_iam_role_name<service>-tasksRole name
tasks_iam_role_use_name_prefixtrueUse a name prefix
tasks_iam_role_policies{}Managed policies to attach
tasks_iam_role_statementsnullInline policy statements
tasks_iam_role_permissions_boundarynullPermissions boundary ARN
tasks_iam_role_max_session_durationnullMaximum session duration in seconds

Service IAM role

The service IAM role is assumed by the ECS service (ecs.amazonaws.com) and is used to register and deregister task IPs or instances with Elastic Load Balancing target groups or classic load balancers.
This role is only required when the service uses a load balancer and the task network mode is not awsvpc. For Fargate tasks (which always use awsvpc), the load balancer registers task ENI IPs directly and this role is not needed. The module automatically determines whether to create it based on network_mode and load_balancer.
The condition for creation in the service module is:
needs_iam_role  = var.network_mode != "awsvpc" && var.load_balancer != null
create_iam_role = var.create && var.create_iam_role && local.needs_iam_role
The service IAM role receives a policy with the following permissions:
StatementActions
ECSServiceec2:Describe*, elasticloadbalancing:DeregisterInstancesFromLoadBalancer, elasticloadbalancing:DeregisterTargets, elasticloadbalancing:Describe*, elasticloadbalancing:RegisterInstancesWithLoadBalancer, elasticloadbalancing:RegisterTargets
Additional statements can be added via iam_role_statements.

Controlling variables

VariableDefaultDescription
create_iam_roletrueWhether to create the service IAM role
iam_role_arnnullARN of a pre-existing role to use
iam_role_name<service>Role name
iam_role_use_name_prefixtrueUse a name prefix
iam_role_statementsnullAdditional inline policy statements
iam_role_permissions_boundarynullPermissions boundary ARN

Infrastructure IAM role

The infrastructure IAM role is used by ECS to manage the underlying EC2 compute fleet when using the ECS Managed Instances capacity provider type. It is assumed by the ECS service principal (ecs.amazonaws.com) and allows ECS to create and manage EC2 launch templates, run EC2 instances, and tag resources. This role is created by the cluster sub-module and is only created when at least one capacity provider in capacity_providers has a managed_instances_provider block configured.
locals {
  create_infrastructure_iam_role = (
    var.create &&
    var.create_infrastructure_iam_role &&
    local.managed_instances_enabled  # true when any capacity provider uses managed_instances_provider
  )
}
The module creates a custom policy (rather than using AmazonECSInfrastructureRolePolicyForManagedInstances) to avoid a surprising AWS requirement that the role name start with ecsInstanceRole when using the managed policy.Key permission groups:
StatementActionsNotes
CreateLaunchTemplateForManagedInstancesec2:CreateLaunchTemplateRestricted to resources tagged AmazonECSManaged=true
CreateLaunchTemplateVersionsForManagedInstancesec2:CreateLaunchTemplateVersion, ec2:ModifyLaunchTemplateRestricted to ECS-managed resources
ProvisionEC2InstancesForManagedInstancesec2:CreateFleetFleet, instance, network interface, launch template, volume
RunInstancesForManagedInstancesec2:RunInstancesInstance, volume, network interface
TagOnCreateEC2ResourcesForManagedInstancesec2:CreateTagsOnly on CreateFleet, CreateLaunchTemplate, RunInstances
PassInstanceRoleForManagedInstancesiam:PassRolePasses the node IAM role to EC2
CreateServiceLinkedRoleForEC2Spotiam:CreateServiceLinkedRoleFor EC2 Spot service-linked role
DescribeEC2ResourcesManagedByECSec2:Describe*Read-only discovery of VPC, subnet, SG, instance resources

Controlling variables

VariableDefaultDescription
create_infrastructure_iam_roletrueWhether to create the role (only applies when Managed Instances is enabled)
infrastructure_iam_role_name<cluster>-infraRole name
infrastructure_iam_role_use_name_prefixtrueUse a name prefix
infrastructure_iam_role_source_policy_documentsnullSource policy documents to merge
infrastructure_iam_role_override_policy_documentsnullOverride policy documents
infrastructure_iam_role_statementsnullAdditional inline policy statements
infrastructure_iam_role_permissions_boundarynullPermissions boundary ARN

Node IAM role

The node IAM role is attached to EC2 instances launched by the ECS Managed Instances capacity provider via an instance profile. It is assumed by ec2.amazonaws.com and grants the ECS agent running on each node the permissions it needs to register with the cluster, poll for tasks, and report state. Like the infrastructure role, the node IAM role is created by the cluster sub-module and is only created when Managed Instances is in use.
AWS documentation states that when using the AWS-managed AmazonECSInstanceRolePolicyForManagedInstances policy, the instance profile must be named ecsInstanceRole. This module avoids that constraint by creating a custom equivalent policy, allowing any name.
The module creates a custom policy equivalent to AmazonECSInstanceRolePolicyForManagedInstances:
StatementActionsResources
ECSAgentDiscoverPollEndpointPermissionsecs:DiscoverPollEndpoint*
ECSAgentRegisterPermissionsecs:RegisterContainerInstanceThe specific cluster ARN
ECSAgentPollPermissionsecs:PollContainer instances in the account
ECSAgentTelemetryPermissionsecs:StartTelemetrySession, ecs:PutSystemLogEventsContainer instances in the account
ECSAgentStateChangePermissionsecs:SubmitAttachmentStateChanges, ecs:SubmitTaskStateChangeThe specific cluster ARN
Additional policies can be attached via node_iam_role_additional_policies. Custom statements can be added via node_iam_role_statements.An aws_iam_instance_profile resource is created alongside the role so that EC2 instances launched by the Managed Instances fleet can use it.

Controlling variables

VariableDefaultDescription
create_node_iam_instance_profiletrueWhether to create the node role and instance profile
node_iam_role_name<cluster>-nodeRole name
node_iam_role_use_name_prefixtrueUse a name prefix
node_iam_role_additional_policies{}Additional managed policies to attach
node_iam_role_source_policy_documentsnullSource policy documents to merge
node_iam_role_override_policy_documentsnullOverride policy documents
node_iam_role_statementsnullAdditional inline policy statements
node_iam_role_permissions_boundarynullPermissions boundary ARN

Build docs developers (and LLMs) love