AWS ECS

Learn how to access Doppler secrets in your ECS tasks

Prerequisites

Sync your secrets to AWS Secrets Manager

The first step to using Doppler with your AWS ECS tasks is to create a new AWS Secrets Manager sync that uses the Single-Secret sync strategy. You can sync to whatever secret name you want, but for the purposes of this explanation, we'll be syncing to /doppler/backend/dev.

Create IAM policy and role

Create a new IAM policy that provides access to the new secret Doppler created. It should look something like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": [
                "arn:aws:secretsmanager:region:aws_account_id:secret:/doppler/backend/dev-Fz0eca"
            ]
        }
    ]
}

Add this new policy to your task's IAM execution role or create a new one if you don't already have one.

Update task definition

Next, you need to update your task definition to pull the secret from AWS Secrets Manager and set it as an environment variable in the containers your task spawns. We'll use an example task definition from AWS' documentation to demonstrate:

{
    "executionRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskExecutionRole",
    "containerDefinitions": [
        {
            "entryPoint": [
                "sh",
                "-c"
            ],
            "portMappings": [
                {
                    "hostPort": 80,
                    "protocol": "tcp",
                    "containerPort": 80
                }
            ],
            "command": [
                "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' >  /usr/local/apache2/htdocs/index.html && httpd-foreground\""
            ],
            "cpu": 10,
            "secrets": [
                {
                    "valueFrom": "arn:aws:secretsmanager:region:aws_account_id:secret:/doppler/backend/dev-Fz0eca",
                    "name": "DOPPLER_SECRETS"
                }
            ],
            "memory": 300,
            "image": "public.ecr.aws/docker/library/httpd:2.4",
            "essential": true,
            "name": "ecs-secrets-container"
        }
    ],
    "family": "ecs-secrets-tutorial"
}

The valueFrom entry should be the ARN for the secret Doppler is managing (/doppler/backend/dev in this example). The name entry is the environment variable that will contain the value of the secret. The Single-Secret sync strategy syncs all your secrets as JSON, which means this one secret will contain all your secrets as JSON.

Parse secret in application

Now all your secrets are being injected into the DOPPLER_SECRETS environment variable as a JSON string. To access this, you'll want to parse the secret inside your application.

import os
import json

secrets = json.loads(os.environ['DOPPLER_SECRETS'])
api_key = secrets['API_KEY']
print(api_key)
const secrets = JSON.parse(process.env.DOPPLER_SECRETS || '{}');
const apiKey = secrets.API_KEY;
console.log(apiKey);
require "json"

secrets = JSON.parse(ENV["DOPPLER_SECRETS"])
api_key = secrets["API_KEY"]
puts api_key
package main

import (
	"encoding/json"
	"fmt"
	"os"
)

func main() {
	dopplerSecrets := os.Getenv("DOPPLER_SECRETS")

	if dopplerSecrets == "" {
		fmt.Println("DOPPLER_SECRETS environment variable isn't set")
		return
	}

	var secrets map[string]interface{}
	err := json.Unmarshal([]byte(dopplerSecrets), &secrets)
	if err != nil {
		fmt.Printf("Error parsing DOPPLER_SECRETS: %s\n", err)
		return
	}

	apiKey, ok := secrets["API_KEY"]
	if ok {
		fmt.Printf("API_KEY: %v\n", apiKey)
	} else {
		fmt.Println("API_KEY not found in secrets")
	}
}

// == Maven Dependency ==
//
// <dependency>
//    <groupId>com.fasterxml.jackson.core</groupId>
//    <artifactId>jackson-databind</artifactId>
//    <version>2.18.3</version> <!-- Use the latest version -->
// </dependency>

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Map;

public class DopplerSecretsParser {
    public static void main(String[] args) {
        String dopplerSecrets = System.getenv("DOPPLER_SECRETS");

        if (dopplerSecrets == null || dopplerSecrets.isEmpty()) {
            System.out.println("DOPPLER_SECRETS environment variable isn't set");
            return;
        }

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode secretsNode = objectMapper.readTree(dopplerSecrets);

            if (secretsNode.has("API_KEY")) {
                String apiKey = secretsNode.get("API_KEY").asText();
                System.out.println("API_KEY: " + apiKey);
            } else {
                System.out.println("API_KEY not found in secrets");
            }
        } catch (Exception e) {
            System.out.println("Error parsing DOPPLER_SECRETS: " + e.getMessage());
        }
    }
}