# Fix AWS Secrets Manager Access Denied

Your application or Lambda function can't retrieve secrets from Secrets Manager. You get "AccessDenied" or "User is not authorized to perform: secretsmanager:GetSecretValue" errors. The secret exists, you're authenticated, but something is blocking access to your sensitive data.

Secrets Manager access requires proper IAM permissions, and optionally resource-based policies. Let's diagnose and fix the access denied issues.

Diagnosis Commands

First, verify who you're authenticated as:

bash
aws sts get-caller-identity

Check if the secret exists:

bash
aws secretsmanager list-secrets \
  --query 'SecretList[*].[Name,ARN]'

Get secret metadata:

bash
aws secretsmanager describe-secret \
  --secret-id my-secret \
  --query '[ARN,Name,Description,SecretVersionsToStages]'

Try to retrieve the secret value:

bash
aws secretsmanager get-secret-value \
  --secret-id my-secret \
  --query 'SecretString'

Check your IAM policies:

```bash aws iam list-user-policies \ --user-name my-user

aws iam list-attached-user-policies \ --user-name my-user ```

Get specific policy content:

bash
aws iam get-user-policy \
  --user-name my-user \
  --policy-name my-policy \
  --query 'PolicyDocument'

For roles, check role policies:

```bash aws iam list-role-policies \ --role-name my-role

aws iam get-role-policy \ --role-name my-role \ --policy-name secrets-policy ```

Check if the secret has a resource-based policy:

bash
aws secretsmanager get-resource-policy \
  --secret-id my-secret \
  --query 'ResourcePolicy'

Use policy simulator to test permissions:

bash
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/my-user \
  --action-names secretsmanager:GetSecretValue \
  --resource-arns arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret-abc123

Common Causes and Solutions

Missing IAM Policy Permissions

The identity doesn't have permission to access secrets:

bash
aws iam get-role-policy \
  --role-name my-role \
  --policy-name my-policy

Add secrets manager permissions:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret-*"
    }
  ]
}

Apply the policy:

bash
aws iam put-role-policy \
  --role-name my-role \
  --policy-name SecretsAccess \
  --policy-document file://secrets-policy.json

For more specific access by secret name:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": [
        "arn:aws:secretsmanager:us-east-1:123456789012:secret:database-credentials-*",
        "arn:aws:secretsmanager:us-east-1:123456789012:secret:api-keys-*"
      ]
    }
  ]
}

Resource Policy Blocking Access

Secrets Manager supports resource-based policies that can restrict access:

bash
aws secretsmanager get-resource-policy \
  --secret-id my-secret \
  --query 'ResourcePolicy' \
  --output text | jq .

If there's a Deny statement, remove or modify it:

bash
aws secretsmanager delete-resource-policy \
  --secret-id my-secret

Or put a more permissive policy:

bash
aws secretsmanager put-resource-policy \
  --secret-id my-secret \
  --resource-policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::123456789012:role/my-role"
        },
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "*"
      }
    ]
  }'

Cross-Account Access Issues

To access a secret in another account, both sides need configuration:

  1. 1.In Account B (secret owner):
  2. 2.Add resource policy allowing Account A:
bash
aws secretsmanager put-resource-policy \
  --secret-id shared-secret \
  --resource-policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::ACCOUNT-A-ID:root"
        },
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "*"
      }
    ]
  }'
  1. 1.In Account A (accessing account):
  2. 2.Add IAM policy allowing access to cross-account secret:
bash
aws iam put-role-policy \
  --role-name my-role \
  --policy-name CrossAccountSecrets \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "arn:aws:secretsmanager:us-east-1:ACCOUNT-B-ID:secret:shared-secret-*"
      }
    ]
  }'

Wrong Secret ARN

Secret ARNs include a random suffix after the name:

bash
aws secretsmanager describe-secret \
  --secret-id my-secret \
  --query 'ARN'

ARN format: arn:aws:secretsmanager:region:account:secret:name-6chars

The -6chars suffix is mandatory in the ARN. Use wildcard in IAM policies:

json
{
  "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret-*"
}

Or use the secret name (not ARN) in CLI commands:

bash
aws secretsmanager get-secret-value --secret-id my-secret

Version Stage Restrictions

Secret has multiple versions and you're accessing the wrong stage:

bash
aws secretsmanager describe-secret \
  --secret-id my-secret \
  --query 'SecretVersionsToStages'

Default stages: AWSCURRENT and AWSPENDING

Specify stage when retrieving:

bash
aws secretsmanager get-secret-value \
  --secret-id my-secret \
  --version-stage AWSCURRENT

KMS Key Access Issues

If the secret is encrypted with a custom KMS key:

bash
aws secretsmanager describe-secret \
  --secret-id my-secret \
  --query 'KmsKeyId'

You need KMS decrypt permission:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret-*",
        "arn:aws:kms:us-east-1:123456789012:key/my-key-id"
      ]
    }
  ]
}

Check KMS key policy:

bash
aws kms get-key-policy \
  --key-id my-key-id \
  --policy-name default \
  --query 'Policy' \
  --output text | jq .

Lambda Execution Role

For Lambda functions accessing secrets:

bash
aws lambda get-function-configuration \
  --function-name my-function \
  --query 'Role'

Check role's policies:

bash
ROLE_NAME=$(aws lambda get-function-configuration --function-name my-function --query 'Role' --output text | cut -d'/' -f2)
aws iam list-role-policies --role-name $ROLE_NAME

Add secrets policy to execution role:

bash
aws iam put-role-policy \
  --role-name $ROLE_NAME \
  --policy-name SecretsAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "arn:aws:secretsmanager:*:*:secret:*"
      }
    ]
  }'

Permission Boundaries

IAM permission boundaries can limit maximum permissions:

bash
aws iam get-role \
  --role-name my-role \
  --query 'Role.PermissionsBoundary'

If a boundary is set, ensure it allows secretsmanager actions:

```bash aws iam get-policy \ --policy-arn arn:aws:iam::123456789012:policy/my-boundary \ --query 'Policy.DefaultVersionId'

aws iam get-policy-version \ --policy-arn arn:aws:iam::123456789012:policy/my-boundary \ --version-id v1 \ --query 'Document' ```

Secrets Rotation Lambda

For secrets with rotation, the rotation Lambda needs special permissions:

bash
aws secretsmanager describe-secret \
  --secret-id my-secret \
  --query 'RotationConfiguration.LambdaFunctionARN'

Check rotation Lambda permissions:

bash
aws lambda get-function-configuration \
  --function-name my-rotation-function \
  --query 'Role'

The rotation Lambda needs: - secretsmanager:GetSecretValue - secretsmanager:PutSecretValue - secretsmanager:UpdateSecretVersionStage - Permissions to update the target resource (database, service, etc.)

Request Denied by Secrets Manager

Some requests are blocked by Secrets Manager's internal rules:

bash
# Check CloudTrail for denied events
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
  --max-items 20 \
  --query 'Events[*].[EventTime,Username,Resources[0].ResourceName,ErrorCode]'

Verification Steps

After making changes, verify access:

bash
aws secretsmanager get-secret-value \
  --secret-id my-secret \
  --query '[Name,ARN,SecretString]'

Test from Lambda or application:

```python import boto3

client = boto3.client('secretsmanager') try: response = client.get_secret_value(SecretId='my-secret') print(f"Successfully retrieved secret: {response['Name']}") except client.exceptions.AccessDeniedException as e: print(f"Access denied: {e}") ```

Create diagnostic script:

```bash #!/bin/bash SECRET_ID="my-secret"

echo "Secrets Manager Access Diagnostics" echo "=================================="

echo "1. Caller Identity:" aws sts get-caller-identity

echo "" echo "2. Secret Exists:" aws secretsmanager describe-secret \ --secret-id $SECRET_ID \ --query '[Name,ARN,Description]'

echo "" echo "3. Secret Resource Policy:" aws secretsmanager get-resource-policy \ --secret-id $SECRET_ID \ --query 'ResourcePolicy' \ --output text | jq . || echo "No resource policy"

echo "" echo "4. KMS Key (if custom):" KMS_KEY=$(aws secretsmanager describe-secret --secret-id $SECRET_ID --query 'KmsKeyId' --output text) if [ "$KMS_KEY" != "null" ] && [ -n "$KMS_KEY" ]; then echo "Custom KMS Key: $KMS_KEY" aws kms describe-key --key-id $KMS_KEY --query 'KeyMetadata.[KeyId,KeyState,Description]' else echo "Using default Secrets Manager encryption" fi

echo "" echo "5. Direct Access Test:" aws secretsmanager get-secret-value \ --secret-id $SECRET_ID \ --query '[Name,VersionId,VersionStages]' 2>&1

echo "" echo "6. Recent Access Denied Events:" aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --query 'Events[?ErrorCode!=null].[EventTime,Username,ErrorCode,ErrorMessage]' ```

Set up monitoring for secret access:

bash
aws cloudwatch put-metric-alarm \
  --alarm-name secrets-access-denied \
  --alarm-description "Secrets Manager access denied" \
  --namespace AWS/SecretsManager \
  --metric-name AccessDenied \
  --dimensions Name=SecretName,Value=my-secret \
  --statistic Sum \
  --period 60 \
  --threshold 1 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts