ECS Container Secrets Without Execution Role Access
TL-PERM-004
What this rule checks
An ECS container definition pulls Secrets[].ValueFrom from an in-template Secrets Manager secret or SSM parameter, but the task EXECUTION role (which the ECS agent uses to resolve secrets) lacks secretsmanager:GetSecretValue / ssm:GetParameters. Containers fail to start with ResourceInitializationError.
How to fix it
- 1Use ecs.Secret.fromSecretsManager() / ecs.Secret.fromSsmParameter() in the container definition β CDK grants the execution role automatically
- 2Or grant manually: secret.grantRead(taskDefinition.executionRole)
- 3Note it is the EXECUTION role that resolves container secrets, not the task role
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
const dbSecret = new secretsmanager.Secret(this, 'DbSecret');
const executionRole = new iam.Role(this, 'ExecRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});
new ecs.CfnTaskDefinition(this, 'Task', {
family: 'app',
requiresCompatibilities: ['FARGATE'],
cpu: '256',
memory: '512',
networkMode: 'awsvpc',
executionRoleArn: executionRole.roleArn,
containerDefinitions: [
{
name: 'app',
image: 'public.ecr.aws/docker/library/busybox:latest',
secrets: [{ name: 'DB_PASSWORD', valueFrom: dbSecret.secretArn }],
},
],
});import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
const dbSecret = new secretsmanager.Secret(this, 'DbSecret');
const taskDefinition = new ecs.FargateTaskDefinition(this, 'Task', {
cpu: 256,
memoryLimitMiB: 512,
});
taskDefinition.addContainer('app', {
image: ecs.ContainerImage.fromRegistry(
'public.ecr.aws/docker/library/busybox:latest'
),
secrets: { DB_PASSWORD: ecs.Secret.fromSecretsManager(dbSecret) },
});CDK Insights pinpoints the exact file and line in your CDK source for every finding, so you can jump straight to the fix.
Affected resource types
AWS::ECS::TaskDefinitionAWS::SecretsManager::SecretAWS::SSM::ParameterAWS::IAM::RoleIntentional? Suppress this finding
Sometimes a flag is deliberate β a genuinely public endpoint, say. You can dismiss TL-PERM-004 and the reason is kept in the report, not silently hidden.
In .cdk-insights.json:
{
"ignoreRules": [
{ "id": "TL-PERM-004", "reason": "Why this is intentional" }
]
}Or inline in your CDK code:
Validations.of(scope).acknowledge({
id: 'cdk-insights::TL-PERM-004',
reason: 'Why this is intentional',
});Use the rule ID TL-PERM-004 shown above β not the CDK-* ID from SARIF / GitHub code scanning. To dismiss every finding on one construct instead, use ignorePaths. Suppression docs β
Catch this in your stack
$ npx cdk-insights scanCDK Insights runs this and 131+ other rules locally against your synthesised CDK app β free, no account, your code never leaves your machine.