Storing sensitive information such as database passwords, API keys, and authentication tokens directly in ECS container environment variables creates significant security risks and compliance violations. This FinOps policy helps organizations identify and remediate insecure secret management practices in their ECS infrastructure while reducing potential security breaches.
Why this policy matters
Container environment variables in ECS task definitions are stored in plaintext and lack the security controls necessary for protecting sensitive data. When secrets are exposed through environment variables, they become accessible through multiple attack vectors including shell access, application logs, process listings, and the /proc filesystem. This exposure creates substantial financial and operational risks for organizations.
Implementation Guide
Infrastructure-as-Code Example (Terraform)
# Problematic configuration - secrets in environment variables
resource "aws_ecs_task_definition" "bad_example" {
family = "my-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.execution_role.arn
task_role_arn = aws_iam_role.task_role.arn
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> environment = [<br> {<br> name = "DATABASE_PASSWORD"<br> value = "super-secret-password" # Security risk!<br> },<br> {<br> name = "API_KEY"<br> value = "abc123xyz789" # Security risk!<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Secure configuration using AWS Secrets Manager</h1>
<p>resource "aws_secretsmanager_secret" "database_password" {<br> name = "my-app/database-password"<br> description = "Database password for my-app"<br>}</p>
<p>resource "aws_secretsmanager_secret_version" "database_password" {<br> secret_id = aws_secretsmanager_secret.database_password.id<br> secret_string = "super-secret-password"<br>}</p>
<p>resource "aws_ssm_parameter" "api_key" {<br> name = "/my-app/api-key"<br> type = "SecureString"<br> value = "abc123xyz789"<br>}</p>
<p>resource "aws_ecs_task_definition" "secure_example" {<br> family = "my-app"<br> requires_compatibilities = ["FARGATE"]<br> network_mode = "awsvpc"<br> cpu = 256<br> memory = 512<br> execution_role_arn = aws_iam_role.execution_role.arn<br> task_role_arn = aws_iam_role.task_role.arn</p>
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> secrets = [<br> {<br> name = "DATABASE_PASSWORD"<br> valueFrom = aws_secretsmanager_secret.database_password.arn<br> },<br> {<br> name = "API_KEY"<br> valueFrom = aws_ssm_parameter.api_key.arn<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Required IAM permissions for secrets access</h1>
<p>resource "aws_iam_role_policy" "task_secrets_policy" {<br> name = "task-secrets-policy"<br> role = aws_iam_role.task_role.id</p># Problematic configuration - secrets in environment variables
resource "aws_ecs_task_definition" "bad_example" {
family = "my-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.execution_role.arn
task_role_arn = aws_iam_role.task_role.arn
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> environment = [<br> {<br> name = "DATABASE_PASSWORD"<br> value = "super-secret-password" # Security risk!<br> },<br> {<br> name = "API_KEY"<br> value = "abc123xyz789" # Security risk!<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Secure configuration using AWS Secrets Manager</h1>
<p>resource "aws_secretsmanager_secret" "database_password" {<br> name = "my-app/database-password"<br> description = "Database password for my-app"<br>}</p>
<p>resource "aws_secretsmanager_secret_version" "database_password" {<br> secret_id = aws_secretsmanager_secret.database_password.id<br> secret_string = "super-secret-password"<br>}</p>
<p>resource "aws_ssm_parameter" "api_key" {<br> name = "/my-app/api-key"<br> type = "SecureString"<br> value = "abc123xyz789"<br>}</p>
<p>resource "aws_ecs_task_definition" "secure_example" {<br> family = "my-app"<br> requires_compatibilities = ["FARGATE"]<br> network_mode = "awsvpc"<br> cpu = 256<br> memory = 512<br> execution_role_arn = aws_iam_role.execution_role.arn<br> task_role_arn = aws_iam_role.task_role.arn</p>
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> secrets = [<br> {<br> name = "DATABASE_PASSWORD"<br> valueFrom = aws_secretsmanager_secret.database_password.arn<br> },<br> {<br> name = "API_KEY"<br> valueFrom = aws_ssm_parameter.api_key.arn<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Required IAM permissions for secrets access</h1>
<p>resource "aws_iam_role_policy" "task_secrets_policy" {<br> name = "task-secrets-policy"<br> role = aws_iam_role.task_role.id</p># Problematic configuration - secrets in environment variables
resource "aws_ecs_task_definition" "bad_example" {
family = "my-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.execution_role.arn
task_role_arn = aws_iam_role.task_role.arn
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> environment = [<br> {<br> name = "DATABASE_PASSWORD"<br> value = "super-secret-password" # Security risk!<br> },<br> {<br> name = "API_KEY"<br> value = "abc123xyz789" # Security risk!<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Secure configuration using AWS Secrets Manager</h1>
<p>resource "aws_secretsmanager_secret" "database_password" {<br> name = "my-app/database-password"<br> description = "Database password for my-app"<br>}</p>
<p>resource "aws_secretsmanager_secret_version" "database_password" {<br> secret_id = aws_secretsmanager_secret.database_password.id<br> secret_string = "super-secret-password"<br>}</p>
<p>resource "aws_ssm_parameter" "api_key" {<br> name = "/my-app/api-key"<br> type = "SecureString"<br> value = "abc123xyz789"<br>}</p>
<p>resource "aws_ecs_task_definition" "secure_example" {<br> family = "my-app"<br> requires_compatibilities = ["FARGATE"]<br> network_mode = "awsvpc"<br> cpu = 256<br> memory = 512<br> execution_role_arn = aws_iam_role.execution_role.arn<br> task_role_arn = aws_iam_role.task_role.arn</p>
<p> container_definitions = jsonencode([<br> {<br> name = "my-app"<br> image = "my-app:latest"<br> secrets = [<br> {<br> name = "DATABASE_PASSWORD"<br> valueFrom = aws_secretsmanager_secret.database_password.arn<br> },<br> {<br> name = "API_KEY"<br> valueFrom = aws_ssm_parameter.api_key.arn<br> }<br> ]<br> logConfiguration = {<br> logDriver = "awslogs"<br> options = {<br> awslogs-group = "/ecs/my-app"<br> awslogs-region = "us-east-1"<br> awslogs-stream-prefix = "ecs"<br> }<br> }<br> }<br> ])<br>}</p>
<h1>Required IAM permissions for secrets access</h1>
<p>resource "aws_iam_role_policy" "task_secrets_policy" {<br> name = "task-secrets-policy"<br> role = aws_iam_role.task_role.id</p>
The secure implementation uses the secrets block instead of the environment block for sensitive data. Infracost automatically detects when ECS task definitions use environment variables that appear to contain secrets and flags them for remediation.
Manual Step-by-Step Instructions
Audit existing task definitions: Review all ECS task definitions to identify environment variables containing sensitive data such as passwords, keys, tokens, or connection strings.
Create secrets in AWS Secrets Manager or SSM Parameter Store:
For frequently rotated secrets (database passwords, API tokens): Use AWS Secrets Manager
For configuration parameters that change infrequently: Use SSM Parameter Store with SecureString type
Update IAM roles: Ensure the ECS task role has appropriate permissions to access the secrets from Secrets Manager or Parameter Store.
Modify task definitions: Replace environment blocks containing secrets with secrets blocks that reference the ARNs of stored secrets.
Test the deployment: Verify that applications can successfully retrieve secrets and function correctly.
Remove old secrets: Clean up any hardcoded secrets from previous configurations and rotate them if they were previously exposed.
Best Practices
Use AWS Secrets Manager for secrets that require automatic rotation
Implement least-privilege IAM policies for secret access
Enable CloudTrail logging for secret access auditing
Use consistent naming conventions for secrets across environments
Implement secret scanning in CI/CD pipelines to prevent future violations
Regular audit secret access patterns and remove unused permissions
Tools and Scripts
Infracost supports this policy in its FinOps policy engine, including in the free trial. The platform automatically scans ECS task definitions and identifies when environment variables contain potential secrets based on variable names and patterns. Organizations can use Infracost to continuously monitor their infrastructure-as-code for secret management violations and track remediation progress over time.
Considerations and Caveats
While this policy generally applies to all sensitive data, there are some considerations:
Performance impact: Retrieving secrets from external services adds minimal latency compared to environment variables, but this should be considered for high-frequency operations
Cost implications: AWS Secrets Manager charges per secret per month, while SSM Parameter Store has different pricing tiers
Legacy applications: Some older applications may require code changes to support retrieving secrets from external sources
Development environments: Consider using different secret management strategies for development versus production environments
Network dependencies: Applications must have network access to AWS APIs to retrieve secrets
Frequently Asked Questions
Is this policy supported in Infracost?
Yes, this policy is available in Infracost, including in the free trial. The platform automatically detects environment variables that appear to contain secrets and provides recommendations for remediation.
What types of environment variables does this policy flag?
The policy identifies environment variables with names containing keywords like 'password', 'key', 'token', 'secret', and 'credential', as well as variables with values that match common secret patterns.
Should I use AWS Secrets Manager or Systems Manager Parameter Store?
Use Secrets Manager for secrets that require automatic rotation or cross-service access. Use Systems Manager Parameter Store for configuration values that change less frequently and when cost optimization is a priority.
How does Infracost help with ongoing compliance?
Infracost continuously monitors infrastructure-as-code repositories and integrates with CI/CD pipelines to prevent new violations from being deployed, enabling FinOps teams to track progress and maintain compliance over time.
What about non-sensitive environment variables?
Non-sensitive values such as feature flags, app settings, or public endpoints can safely remain as environment variables. The policy specifically targets variables that appear to contain sensitive data.
Can this policy be customized for different environments?
Yes, the policy can be configured with different sensitivity levels for development, staging, and production environments to match your organization's risk tolerance.
How often should I review secret management practices?
Review secret management practices monthly as part of your security audit process, and implement automated scanning in your CI/CD pipeline to catch violations before they reach production.