Skip to content

AWS Secrets Manager — Complete Implementation Guide

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about AWS Secrets Manager. We cover key concepts, practical examples, and best practices to help you master this topic.

AWS Secrets Manager provides centralized secret storage with automatic rotation, fine-grained IAM access control, secret versioning, and native integration with AWS services like RDS, Lambda, and ECS.

What You'll Learn

By the end of this tutorial, you will know how to store and retrieve secrets from AWS Secrets Manager, configure automatic rotation, control access with IAM policies, and integrate with AWS services.

Why It Matters

AWS Secrets Manager is the default secrets management solution for applications running on AWS. It integrates natively with AWS services and eliminates the operational overhead of running Vault.

Real-World Use

DodaTech's AWS-hosted services use Secrets Manager for all secrets. RDS databases have automatic credential rotation every 30 days, and Lambda functions access secrets via IAM roles.

AWS Secrets Manager Learning Path

flowchart LR
  A[HashiCorp Vault] --> B[AWS Secrets Manager]
  B --> C[Store & Retrieve]
  B --> D[Rotation]
  B --> E[IAM Access]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Storing and Retrieving Secrets

Use the AWS SDK to store and retrieve secrets programmatically.

const {
  SecretsManagerClient,
  GetSecretValueCommand,
  CreateSecretCommand,
  UpdateSecretCommand
} = require("@aws-sdk/client-secrets-manager");

class AWSSecretsManager {
  constructor(region = "us-east-1") {
    this.client = new SecretsManagerClient({ region });
  }

  async getSecret(secretId) {
    try {
      const command = new GetSecretValueCommand({ SecretId: secretId });
      const response = await this.client.send(command);

      if (response.SecretString) {
        return JSON.parse(response.SecretString);
      }

      throw new Error("Secret is binary, use getBinarySecret() instead");
    } catch (err) {
      if (err.name === "ResourceNotFoundException") {
        throw new Error(`Secret ${secretId} not found`);
      }
      if (err.name === "AccessDeniedException") {
        throw new Error(`Access denied to secret ${secretId}`);
      }
      throw err;
    }
  }

  async createSecret(name, secretValue, description = "") {
    const command = new CreateSecretCommand({
      Name: name,
      SecretString: JSON.stringify(secretValue),
      Description: description
    });

    const response = await this.client.send(command);
    console.log(`Created secret: ${name} (${response.ARN})`);
    return response;
  }

  async updateSecret(secretId, secretValue) {
    const command = new UpdateSecretCommand({
      SecretId: secretId,
      SecretString: JSON.stringify(secretValue)
    });

    const response = await this.client.send(command);
    console.log(`Updated secret: ${secretId} (version ${response.VersionId})`);
    return response;
  }
}

const secretsManager = new AWSSecretsManager();
secretsManager.getSecret("prod/myapp/db").then(secret => {
  console.log("Retrieved database credentials");
});

Automatic Rotation

Secrets Manager can automatically rotate secrets on a schedule.

// Lambda rotation function
exports.handler = async (event) => {
  const arn = event.SecretId;
  const token = event.ClientRequestToken;
  const step = event.Step;

  const { SecretsManager } = require("@aws-sdk/client-secrets-manager");
  const sm = new SecretsManager();

  switch (step) {
    case "createSecret":
      await createSecret(sm, arn, token);
      break;
    case "setSecret":
      await setSecret(sm, arn, token);
      break;
    case "testSecret":
      await testSecret(sm, arn, token);
      break;
    case "finishSecret":
      await finishSecret(sm, arn, token);
      break;
    default:
      throw new Error("Invalid step");
  }

  return { status: "ok" };
};

async function createSecret(sm, arn, token) {
  const crypto = require("crypto");
  const password = crypto.randomBytes(16).toString("hex");

  await sm.putSecretValue({
    SecretId: arn,
    ClientRequestToken: token,
    SecretString: JSON.stringify({
      username: "admin",
      password: password
    }),
    VersionStages: ["AWSPENDING"]
  });
}

async function setSecret(sm, arn, token) {
  // Update the database user's password to the new value
  const secret = await sm.getSecretValue({
    SecretId: arn,
    VersionId: token,
    VersionStage: "AWSPENDING"
  });

  const creds = JSON.parse(secret.SecretString);
  // Update database password: ALTER USER admin PASSWORD 'new-password';
  console.log(`Setting database password to: ${creds.password}`);
}

async function testSecret(sm, arn, token) {
  // Test the new credentials work
  console.log("Testing new credentials...");
}

async function finishSecret(sm, arn, token) {
  // Mark the new version as current
  await sm.updateSecretVersionStage({
    SecretId: arn,
    VersionStage: "AWSCURRENT",
    MoveToVersionId: token,
    RemoveFromVersionId: "AWSPENDING"
  });
  console.log("Rotation complete");
}

IAM Access Control

Control which applications and users can access which secrets.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": [
        "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "secretsmanager:ListSecrets",
      "Resource": "*"
    }
  ]
}
// IAM role for ECS task
const iamPolicy = {
  Version: "2012-10-17",
  Statement: [
    {
      Effect: "Allow",
      Action: [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      Resource: [
        "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/*"
      ]
    }
  ]
};

console.log("IAM policy for secrets access:", iamPolicy.Statement[0].Resource);

Caching Secrets

Cache secrets to reduce API calls and improve performance.

class CachedSecretsManager {
  constructor(region, options = {}) {
    this.client = new (require("@aws-sdk/client-secrets-manager").SecretsManagerClient)({ region });
    this.cache = new Map();
    this.cacheTTL = options.cacheTTL || 300000; // 5 minutes
  }

  async getSecret(secretId) {
    const cached = this.cache.get(secretId);
    if (cached && Date.now() < cached.expiresAt) {
      console.log(`Cache hit for ${secretId}`);
      return cached.value;
    }

    console.log(`Cache miss for ${secretId}, fetching from AWS`);
    const command = new (require("@aws-sdk/client-secrets-manager").GetSecretValueCommand)({ SecretId: secretId });
    const response = await this.client.send(command);

    const value = JSON.parse(response.SecretString);

    this.cache.set(secretId, {
      value,
      expiresAt: Date.now() + this.cacheTTL
    });

    return value;
  }

  invalidateCache(secretId) {
    this.cache.delete(secretId);
    console.log(`Cache invalidated for ${secretId}`);
  }
}

const cachedSM = new CachedSecretsManager("us-east-1", { cacheTTL: 60000 });
cachedSM.getSecret("prod/myapp/db").then(secret => {
  console.log("Retrieved with caching");
});

Integration with AWS Services

Secrets Manager integrates natively with many AWS services.

class AWSIntegrationExamples {
  static ecsTaskDefinition() {
    return {
      family: "my-app",
      taskRoleArn: "arn:aws:iam::123456789012:role/MyAppTaskRole",
      executionRoleArn: "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
      containerDefinitions: [
        {
          name: "app",
          image: "myapp:latest",
          secrets: [
            {
              name: "DB_PASSWORD",
              valueFrom: "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-abc123:password::"
            },
            {
              name: "DB_HOST",
              valueFrom: "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-abc123:host::"
            }
          ]
        }
      ]
    };
  }

  static lambdaEnvironmentVariable(secretArn) {
    return {
      Variables: {
        DB_SECRET: secretArn
      }
    };
  }

  static rdsRotationConfiguration() {
    return {
      rotationInterval: "30d",
      rotationLambdaARN: "arn:aws:lambda:us-east-1:123456789012:function:rotate-db-password",
      automaticallyRotateAfterDays: 30
    };
  }
}

console.log("AWS integration examples:", Object.keys(AWSIntegrationExamples).length);

Common Mistakes

  1. Not using IAM roles for access -- Hardcoding AWS credentials in the application defeats the purpose of Secrets Manager. Use IAM roles for EC2, ECS, or Lambda.

  2. Storing entire secrets as a single string -- Store secrets as structured JSON (username/password as separate fields) for easier individual field access in ECS task definitions.

  3. Not caching secrets -- Every GetSecretValue call costs money and adds latency. Cache secrets in memory with a TTL.

  4. Using Secrets Manager for non-secret configuration -- Secrets Manager costs $0.40 per secret per month. Use it only for secrets, not regular configuration.

  5. Not planning for rotation -- Applications must handle credential rotation gracefully. The database connection string changes when the password rotates.

Practice Questions

  1. How does AWS Secrets Manager rotation work? A Lambda function performs four steps: create new secret version, set the new credentials on the target service, test them, and mark the new version as current.

  2. How do you control access to secrets in AWS Secrets Manager? Using IAM policies that grant specific actions (GetSecretValue) on specific secret ARNs.

  3. What is the cost model for AWS Secrets Manager? $0.40 per secret per month plus $0.05 per 10,000 API calls. Rotation Lambda calls are additional.

  4. Challenge: Implement a secrets manager abstraction that falls back from AWS Secrets Manager to environment variables.

class HybridSecretsProvider {
  constructor(region) {
    this.awsSM = new (require("@aws-sdk/client-secrets-manager").SecretsManagerClient)({ region });
  }

  async getSecret(name) {
    const envVar = process.env[name];
    if (envVar) {
      return JSON.parse(envVar);
    }

    try {
      const command = new (require("@aws-sdk/client-secrets-manager").GetSecretValueCommand)({ SecretId: name });
      const response = await this.awsSM.send(command);
      return JSON.parse(response.SecretString);
    } catch (err) {
      throw new Error(`Secret ${name} not found in env or AWS`);
    }
  }
}

const hybrid = new HybridSecretsProvider("us-east-1");
hybrid.getSecret("DB_CREDENTIALS").then(s => console.log("Secret resolved"));

FAQ

What is the maximum size of a secret in AWS Secrets Manager?

65,536 bytes (64 KB). For larger secrets, store the data in S3 and store the S3 reference in Secrets Manager.

Can I use AWS Secrets Manager with non-AWS services?

Yes. Secrets Manager can store any secret value, not just AWS credentials. Use it for API keys, certificates, and any sensitive data.

How does Secrets Manager handle secret versioning?

Each secret has versions identified by a unique VersionId. Version stages (AWSCURRENT, AWSPENDING, AWSPREVIOUS) track the active version.

What is the difference between AWS Secrets Manager and Parameter Store?

Secrets Manager costs more ($0.40/secret/month vs free) but provides automatic rotation, cross-region replication, and fine-grained access control.

How do I rotate secrets without downtime?

Use the AWSPENDING version stage. The rotation function creates the new version, updates the target, and only marks it as AWSCURRENT after testing.

Mini Project

Build an AWS Secrets Manager wrapper that handles caching, IAM authentication, automatic fallback to environment variables in development, and graceful handling of rotation events.

class AWSSecretsWrapper {
  constructor(region, options = {}) {
    this.region = region;
    this.cache = new Map();
    this.cacheTTL = options.cacheTTL || 300000;
    this.client = new (require("@aws-sdk/client-secrets-manager").SecretsManagerClient)({ region });
  }

  async get(secretId) {
    if (process.env.NODE_ENV === "development") {
      try { return JSON.parse(process.env[secretId]); } catch {}
    }

    const cached = this.cache.get(secretId);
    if (cached && Date.now() < cached.expiresAt) return cached.value;

    const cmd = new (require("@aws-sdk/client-secrets-manager").GetSecretValueCommand)({ SecretId: secretId });
    const res = await this.client.send(cmd);
    const value = JSON.parse(res.SecretString);

    this.cache.set(secretId, { value, expiresAt: Date.now() + this.cacheTTL });
    return value;
  }
}

const wrapper = new AWSSecretsWrapper("us-east-1");
console.log("AWS Secrets Manager wrapper ready");

What's Next

Now that you understand AWS Secrets Manager, learn about Kubernetes ConfigMaps and Secrets. Then explore feature flags.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro