Table of Contents#
- Problem Statement: The CLI Works, but Lambda Doesn’t
- Why CLI vs. Lambda: Key Differences in Execution Context
- Common Causes of AccessDeniedException in Lambda
- Step-by-Step Troubleshooting Guide
- Preventive Measures to Avoid Future Issues
- Conclusion
- References
Problem Statement: The CLI Works, but Lambda Doesn’t#
Let’s set the scene with a concrete example:
Your Lambda Code (Node.js)#
You’ve written a Node.js Lambda function to fetch a sensitive parameter from SSM Parameter Store using the AWS SDK for JavaScript (v3). Here’s a simplified snippet:
import { SSMClient, GetParametersCommand } from "@aws-sdk/client-ssm";
const ssmClient = new SSMClient({ region: "us-east-1" });
export const handler = async (event) => {
try {
const command = new GetParametersCommand({
Names: ["/my-app/db-password"],
WithDecryption: true, // Required if the parameter is encrypted
});
const response = await ssmClient.send(command);
return { statusCode: 200, body: JSON.stringify(response.Parameters) };
} catch (error) {
console.error("Error fetching SSM parameters:", error);
return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
}
};When you test this Lambda, you get:
{ "error": "AccessDeniedException: User: arn:aws:sts::123456789012:assumed-role/MyLambdaRole/MyLambdaFunction is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:us-east-1:123456789012:parameter/my-app/db-password because no identity-based policy allows the ssm:GetParameters action" }The CLI Works#
But when you run the equivalent CLI command, it succeeds:
aws ssm get-parameters --names "/my-app/db-password" --with-decryption --region us-east-1Why does the CLI work while Lambda fails? The answer lies in how AWS authenticates and authorizes each environment.
Why CLI Works but Lambda Doesn’t: Key Differences#
The core issue boils down to IAM identity and context:
| Aspect | AWS CLI | Lambda Function |
|---|---|---|
| IAM Identity | Uses credentials from ~/.aws/credentials (e.g., your IAM user or role) | Uses its execution role (an IAM role assigned at deployment) |
| Permissions Source | Your user/role’s identity-based policies | Lambda execution role’s identity-based policies + resource-based policies (e.g., SSM, KMS) |
| Region/Network Context | Inherits region from CLI config or --region flag | Runs in a specific AWS region (configured at deployment) and may be in a VPC |
The CLI works because your local IAM credentials (user/role) have the necessary permissions to call ssm:GetParameters. Lambda fails because its execution role lacks these permissions—or other critical configurations (e.g., KMS access, regional alignment) are missing.
Common Causes of AccessDeniedException in Lambda#
To resolve the discrepancy, let’s dissect the most likely causes:
1. Lambda Execution Role Lacks ssm:GetParameters Permissions#
The most common culprit: the IAM role assigned to your Lambda function (its “execution role”) does not have the ssm:GetParameters permission.
- Why CLI works: Your CLI user/role has this permission via IAM policies (e.g.,
AmazonSSMFullAccessor a custom policy). - Why Lambda fails: The Lambda execution role is a separate IAM entity and may not inherit your user’s permissions.
2. Mismatched AWS Regions#
SSM parameters are region-specific. If your CLI uses a different region than your Lambda function, the parameter may exist in the CLI’s region but not in Lambda’s region (or vice versa).
- Example: Your CLI runs
--region us-west-2, but your Lambda is deployed tous-east-1. The parameter/my-app/db-passwordexists inus-west-2(CLI succeeds) but not inus-east-1(Lambda fails withAccessDeniedorParameterNotFound).
3. Missing KMS Permissions for Encrypted Parameters#
If your SSM parameter is encrypted with a customer-managed KMS key (not AWS-managed), Lambda needs two sets of permissions:
ssm:GetParameterson the parameter.kms:Decrypton the KMS key used to encrypt the parameter.
- Why CLI works: Your user/role has
kms:Decryptaccess to the key. - Why Lambda fails: The Lambda execution role lacks
kms:Decryptpermissions for the KMS key.
4. SSM Parameter Store Resource Policies Block Access#
AWS Systems Manager (SSM) supports resource-based policies that restrict access to specific parameters. Even if the Lambda execution role has ssm:GetParameters, an SSM resource policy could explicitly deny access.
5. VPC Configuration Issues (Indirect Cause)#
If your Lambda function runs in a VPC without proper network access to SSM, it may fail to reach the SSM service. While this typically causes Timeout or EndpointConnectionError, misconfigured VPC endpoints for SSM can occasionally manifest as AccessDenied (e.g., if the endpoint policy blocks Lambda).
Step-by-Step Troubleshooting Guide#
Follow these steps to diagnose and fix the issue:
Step 1: Confirm the CLI’s Success and Parameter Details#
First, validate that the CLI works and document the parameter’s properties:
# Get parameter details (including region, encryption status, and KMS key)
aws ssm describe-parameters --filters "Name=Name,Values=/my-app/db-password" --region us-east-1Note:
Region: Ensure this matches your Lambda’s region (check Lambda’s “Configuration” tab in the AWS Console).Type: IfType=SecureString, the parameter is encrypted.KeyId: If encrypted with a customer-managed KMS key, note the KMS key ARN (e.g.,arn:aws:kms:us-east-1:123456789012:key/abcd1234).
Step 2: Inspect the Lambda Execution Role’s Permissions#
Lambda uses its execution role for authorization. To check its policies:
- Go to the AWS Lambda Console.
- Select your function → Configuration → Permissions → Execution role.
- Click the role name to open it in the IAM Console.
Check for ssm:GetParameters Permission:
In the IAM role’s “Permissions” tab, verify if any attached policy includes:
{
"Effect": "Allow",
"Action": "ssm:GetParameters",
"Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/my-app/db-password"
}If missing, attach a custom policy to the role (e.g., LambdaSSMGetParametersPolicy):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:GetParameters",
"Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/my-app/db-password"
}
]
}Step 3: Verify Regional Alignment#
Ensure your Lambda function and SSM parameter are in the same region:
- Lambda region: Check the “Configuration” tab in the Lambda Console (e.g.,
us-east-1). - SSM parameter region: Use
aws ssm describe-parameters(as in Step 1) to confirm the parameter exists in Lambda’s region.
If the parameter is in a different region, update your Lambda code to target that region:
// Explicitly set the SSM client region to match the parameter's region
const ssmClient = new SSMClient({ region: "us-west-2" }); // Match parameter's regionStep 4: Check KMS Permissions (for Encrypted Parameters)#
If the parameter is a SecureString encrypted with a customer-managed KMS key:
- Go to the AWS KMS Console and find the key used to encrypt the parameter (from
KeyIdin Step 1). - In the key’s “Key policy” tab, add a statement to allow the Lambda execution role to decrypt:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/MyLambdaExecutionRole" // Lambda's execution role ARN
},
"Action": "kms:Decrypt",
"Resource": "*"
}Step 5: Audit SSM Parameter Store Resource Policies#
SSM allows resource-based policies to restrict parameter access. To check:
- Go to the AWS SSM Console, navigate to “Parameter Store,” and select your parameter.
- Click “Permissions” to view the resource policy. Ensure there’s no
Denystatement blocking your Lambda execution role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/MyLambdaExecutionRole" }, // Blocking Lambda!
"Action": "ssm:GetParameters",
"Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/my-app/db-password"
}
]
}If such a policy exists, remove the Deny or add an explicit Allow for the Lambda role.
Step 6: Validate VPC Configuration (If Lambda Is in a VPC)#
If your Lambda runs in a VPC, ensure it can reach the SSM service:
-
Option 1: Internet access: Attach a NAT gateway to the VPC (requires public subnet).
-
Option 2: VPC Endpoint: Create a VPC endpoint for SSM (
com.amazonaws.<region>.ssm) to avoid internet dependency.- Go to the AWS VPC Console, navigate to “Endpoints,” and create an endpoint for
ssm. - Ensure the endpoint’s security group allows outbound traffic on port 443.
- Go to the AWS VPC Console, navigate to “Endpoints,” and create an endpoint for
Preventive Measures#
To avoid this issue in the future:
- Least Privilege for Lambda Roles: Always grant Lambda execution roles the minimum permissions required (e.g., restrict
ssm:GetParametersto specific parameter ARNs, not*). - Regional Consistency: Deploy Lambda functions and SSM parameters in the same region, or explicitly configure the SDK to target the parameter’s region.
- Document Encryption Context: Track which KMS keys encrypt SSM parameters and ensure Lambda roles have
kms:Decryptaccess to those keys. - Test Permissions with IAM Access Analyzer: Use the IAM Access Analyzer to validate that Lambda roles have access to SSM and KMS resources.
- Monitor with CloudWatch Logs: Enable Lambda logging to CloudWatch and inspect error messages—they often include the exact resource or permission missing (e.g.,
ssm:GetParameterson a specific ARN).
Conclusion#
The AccessDeniedException in Lambda (but not CLI) typically stems from IAM permission gaps, regional mismatches, or missing KMS/SSM resource policies. By systematically verifying the Lambda execution role’s permissions, regional alignment, KMS access, and network configuration, you can resolve the discrepancy.
Remember: the CLI and Lambda use different IAM identities—success with one does not guarantee success with the other. Always test Lambda permissions in the context of its execution role, not your local CLI credentials.