Moving hardcoded secrets to Secrets Manager
The following code example shows how to:
Create IAM roles
Create a secret in Secrets Manager
Update your application code
Update the secret
Clean up resources
- Bash
-
- Amazon CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/bin/bash # Script to move hardcoded secrets to AWS Secrets Manager # This script demonstrates how to create IAM roles, store a secret in AWS Secrets Manager, # and set up appropriate permissions set -euo pipefail # Set up logging LOG_FILE="secrets_manager_tutorial.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting AWS Secrets Manager tutorial script at $(date)" echo "======================================================" # Function to check for errors in command output check_error() { local output=$1 local cmd=$2 if echo "$output" | grep -qi "error"; then echo "ERROR: Command failed: $cmd" echo "$output" cleanup_resources exit 1 fi } # Function to generate a random identifier using secure method generate_random_id() { python3 -c "import secrets; print('sm' + secrets.token_hex(4))" } # Function to safely clean up resources cleanup_resources() { echo "" echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" if [ -n "${SECRET_NAME:-}" ]; then echo "Secret: $SECRET_NAME" fi if [ -n "${RUNTIME_ROLE_NAME:-}" ]; then echo "IAM Role: $RUNTIME_ROLE_NAME" fi if [ -n "${ADMIN_ROLE_NAME:-}" ]; then echo "IAM Role: $ADMIN_ROLE_NAME" fi echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Cleaning up all created resources..." # Delete secret if it exists if [ -n "${SECRET_NAME:-}" ]; then echo "Deleting secret: $SECRET_NAME" aws secretsmanager delete-secret --secret-id "$SECRET_NAME" --force-delete-without-recovery 2>/dev/null || true fi # Detach policies and delete runtime role if it exists if [ -n "${RUNTIME_ROLE_NAME:-}" ]; then echo "Deleting inline policies from runtime role: $RUNTIME_ROLE_NAME" for policy in $(aws iam list-role-policies --role-name "$RUNTIME_ROLE_NAME" --query 'PolicyNames[]' --output text 2>/dev/null || true); do aws iam delete-role-policy --role-name "$RUNTIME_ROLE_NAME" --policy-name "$policy" 2>/dev/null || true done echo "Deleting IAM role: $RUNTIME_ROLE_NAME" aws iam delete-role --role-name "$RUNTIME_ROLE_NAME" 2>/dev/null || true fi # Detach policies and delete admin role if it exists if [ -n "${ADMIN_ROLE_NAME:-}" ]; then echo "Detaching policy from role: $ADMIN_ROLE_NAME" aws iam detach-role-policy --role-name "$ADMIN_ROLE_NAME" --policy-arn "arn:aws:iam::aws:policy/SecretsManagerReadWrite" 2>/dev/null || true for policy in $(aws iam list-role-policies --role-name "$ADMIN_ROLE_NAME" --query 'PolicyNames[]' --output text 2>/dev/null || true); do aws iam delete-role-policy --role-name "$ADMIN_ROLE_NAME" --policy-name "$policy" 2>/dev/null || true done echo "Deleting IAM role: $ADMIN_ROLE_NAME" aws iam delete-role --role-name "$ADMIN_ROLE_NAME" 2>/dev/null || true fi echo "Cleanup completed." } # Trap to ensure cleanup on script exit trap 'echo "Script interrupted. Running cleanup..."; cleanup_resources' INT TERM EXIT # Generate random identifiers for resources ADMIN_ROLE_NAME="SecretsManagerAdmin-$(generate_random_id)" RUNTIME_ROLE_NAME="RoleToRetrieveSecretAtRuntime-$(generate_random_id)" SECRET_NAME="MyAPIKey-$(generate_random_id)" echo "Using the following resource names:" echo "Admin Role: $ADMIN_ROLE_NAME" echo "Runtime Role: $RUNTIME_ROLE_NAME" echo "Secret Name: $SECRET_NAME" echo "" # Step 1: Create IAM roles echo "Creating IAM roles..." # Create assume role policy document ASSUME_ROLE_POLICY=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ) # Create the SecretsManagerAdmin role echo "Creating admin role: $ADMIN_ROLE_NAME" ADMIN_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$ADMIN_ROLE_NAME" \ --assume-role-policy-document "$ASSUME_ROLE_POLICY" 2>&1) check_error "$ADMIN_ROLE_OUTPUT" "create-role for admin" echo "$ADMIN_ROLE_OUTPUT" aws iam tag-role --role-name "$ADMIN_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Attach the SecretsManagerReadWrite policy to the admin role echo "Attaching SecretsManagerReadWrite policy to admin role" ATTACH_POLICY_OUTPUT=$(aws iam attach-role-policy \ --role-name "$ADMIN_ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/SecretsManagerReadWrite" 2>&1) check_error "$ATTACH_POLICY_OUTPUT" "attach-role-policy for admin" echo "Policy attached successfully" # Create the RoleToRetrieveSecretAtRuntime role echo "Creating runtime role: $RUNTIME_ROLE_NAME" RUNTIME_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$RUNTIME_ROLE_NAME" \ --assume-role-policy-document "$ASSUME_ROLE_POLICY" 2>&1) check_error "$RUNTIME_ROLE_OUTPUT" "create-role for runtime" echo "$RUNTIME_ROLE_OUTPUT" aws iam tag-role --role-name "$RUNTIME_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Wait for roles to be fully created echo "Waiting for IAM roles to be fully created..." sleep 10 # Step 2: Create a secret in AWS Secrets Manager echo "Creating secret in AWS Secrets Manager..." # Generate secure secret value using environment variable or secure method # WARNING: In production, use secure methods to inject secrets (AWS CodeBuild, parameter store, etc.) if [ -z "${TUTORIAL_SECRET_VALUE:-}" ]; then SECRET_VALUE=$(python3 -c "import json; print(json.dumps({'ClientID':'my_client_id','ClientSecret':__import__('secrets').token_urlsafe(32)}))") else SECRET_VALUE="$TUTORIAL_SECRET_VALUE" fi CREATE_SECRET_OUTPUT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "API key for my application" \ --secret-string "$SECRET_VALUE" \ --add-replica-regions 'Region=us-east-1' \ --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs 2>&1) check_error "$CREATE_SECRET_OUTPUT" "create-secret" echo "$CREATE_SECRET_OUTPUT" # Get AWS account ID echo "Getting AWS account ID..." ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text 2>&1) check_error "$ACCOUNT_ID" "get-caller-identity" echo "Account ID: $ACCOUNT_ID" # Get secret ARN for precise resource policy echo "Getting secret ARN..." SECRET_ARN=$(aws secretsmanager describe-secret \ --secret-id "$SECRET_NAME" \ --query 'ARN' \ --output text 2>&1) check_error "$SECRET_ARN" "describe-secret" # Add resource policy to the secret with least privilege echo "Adding resource policy to secret..." RESOURCE_POLICY=$(cat <<EOF { "Version":"2012-10-17", "Statement": [ { "Sid": "AllowRuntimeRoleReadSecret", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::$ACCOUNT_ID:role/$RUNTIME_ROLE_NAME" }, "Action": "secretsmanager:GetSecretValue", "Resource": "$SECRET_ARN", "Condition": { "StringEquals": { "secretsmanager:VersionStage": "AWSCURRENT" } } } ] } EOF ) PUT_POLICY_OUTPUT=$(aws secretsmanager put-resource-policy \ --secret-id "$SECRET_NAME" \ --resource-policy "$RESOURCE_POLICY" \ --block-public-policy 2>&1) check_error "$PUT_POLICY_OUTPUT" "put-resource-policy" echo "Resource policy added successfully" # Enable rotation policy recommendation echo "Enabling secret metadata tags for rotation tracking..." aws secretsmanager tag-resource \ --secret-id "$SECRET_NAME" \ --tags Key=Purpose,Value=Tutorial Key=AutoRotation,Value=Recommended 2>/dev/null || true # Step 3: Demonstrate retrieving the secret echo "Retrieving the secret value (for demonstration purposes)..." GET_SECRET_OUTPUT=$(aws secretsmanager get-secret-value \ --secret-id "$SECRET_NAME" 2>&1) check_error "$GET_SECRET_OUTPUT" "get-secret-value" echo "Secret retrieved successfully. Secret metadata:" echo "$GET_SECRET_OUTPUT" | jq '{ARN: .ARN, Name: .Name, LastUpdatedDate: .LastUpdatedDate, VersionIdsToStages: .VersionIdsToStages}' 2>/dev/null || echo "Secret metadata retrieved (jq not available)" # Step 4: Update the secret with new values echo "Updating the secret with new values..." UPDATE_SECRET_VALUE=$(python3 -c "import json; print(json.dumps({'ClientID':'my_new_client_id','ClientSecret':__import__('secrets').token_urlsafe(32)}))") UPDATE_SECRET_OUTPUT=$(aws secretsmanager update-secret \ --secret-id "$SECRET_NAME" \ --secret-string "$UPDATE_SECRET_VALUE" 2>&1) check_error "$UPDATE_SECRET_OUTPUT" "update-secret" echo "Secret updated successfully" # Step 5: Verify the updated secret echo "Verifying the updated secret..." VERIFY_SECRET_OUTPUT=$(aws secretsmanager get-secret-value \ --secret-id "$SECRET_NAME" 2>&1) check_error "$VERIFY_SECRET_OUTPUT" "get-secret-value for verification" echo "Updated secret retrieved successfully. Secret metadata:" echo "$VERIFY_SECRET_OUTPUT" | jq '{ARN: .ARN, Name: .Name, LastUpdatedDate: .LastUpdatedDate, VersionIdsToStages: .VersionIdsToStages}' 2>/dev/null || echo "Secret metadata retrieved (jq not available)" # Step 6: Display rotation recommendations echo "" echo "Rotation Configuration Recommendations:" echo "========================================" DESCRIBE_OUTPUT=$(aws secretsmanager describe-secret --secret-id "$SECRET_NAME" 2>&1) if echo "$DESCRIBE_OUTPUT" | grep -q "RotationRules"; then echo "Current rotation configuration:" echo "$DESCRIBE_OUTPUT" | jq '.RotationRules' 2>/dev/null || echo "Rotation rules available" else echo "No automatic rotation configured. Consider enabling rotation with:" echo "aws secretsmanager rotate-secret --secret-id $SECRET_NAME --rotation-lambda-arn arn:aws:lambda:REGION:ACCOUNT:function:FUNCTION_NAME --rotation-rules AutomaticallyAfterDays=30" fi echo "" echo "======================================================" echo "Tutorial completed successfully!" echo "" echo "Summary of what we did:" echo "1. Created IAM roles for managing and retrieving secrets" echo "2. Created a secret in AWS Secrets Manager with secure generation" echo "3. Added a least-privilege resource policy to control access to the secret" echo "4. Retrieved the secret value (simulating application access)" echo "5. Updated the secret with cryptographically secure values" echo "6. Verified the updated secret" echo "" echo "Security best practices applied:" echo "- Used cryptographically secure random ID generation" echo "- Applied least-privilege resource policies with version stages" echo "- Tagged resources for rotation tracking" echo "- Blocked public access to secrets" echo "- Used ARN-specific permissions instead of wildcards" echo "" echo "Next steps you might want to consider:" echo "- Enable automatic secret rotation with AWS Lambda" echo "- Implement secret caching in your application" echo "- Set up CloudTrail logging for secret access auditing" echo "- Use AWS CodeGuru Reviewer to find hardcoded secrets in your code" echo "- For multi-region applications, replicate your secrets across regions" echo "- Configure VPC endpoints for private access to Secrets Manager" echo "" echo "Script completed at $(date)" exit 0-
For API details, see the following topics in Amazon CLI Command Reference.
-
For a complete list of Amazon SDK developer guides and code examples, see Using this service with an Amazon SDK. This topic also includes information about getting started and details about previous SDK versions.
Manage your account
Permission policy allows Amazon Compute Optimizer Automation to apply recommended actions