The secretsmanager:BatchGetSecretValue action facilitates a principal retrieving the value of multiple secrets through a single API call. When observing the name of the action I wrongfully assumed that you would be able to simply provide a list of ARN’s for your chosen secrets in the resource section of an IAM Policy, with the BatchGetSecretValue action, and away you go, but this is not the case.

When assigning the BatchGetSecretValue action to an IAM Policy, we also need to add the ListSecrets permission to our allowed actions and provide a wildcard value in the resources section. An example policy statement might look like the following:

{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Effect": "Allow",
           "Action": [
               "secretsmanager:BatchGetSecretValue",
               "secretsmanager:ListSecrets"
           ],
           "Resource": "*"
       }
   ]
}

You might at this point be thinking that it’s a little permissive, I know I was, and how will I prevent access to secrets I don’t want to be collected in the BatchGetSecretValue call?

Although not immediately obvious, at least not to me, this policy alone will not provide access to the secret values. You would still receive a 200 response from an API call with only the permissions shown in the example policy above, but there will be nested errors for each secret you don’t have the necessary permissions to access. The way we define those permissions is through the familiar secretsmanager:GetSecretValue action, which we need to assign to each secret individually within the IAM Policy.

The access for each secret is still controlled by GetSecretValue, as it would be when accessing individual secrets. A functional example IAM Policy might look something like this:

{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Effect": "Allow",
           "Action": [
               "secretsmanager:BatchGetSecretValue",
               "secretsmanager:ListSecrets"
           ],
           "Resource": [
               "*"
           ]
       },
       {
           "Effect": "Allow",
           "Action": [
               "secretsmanager:GetSecretValue",
               "secretsmanager:DescribeSecret"
           ],
           "Resource": [
               "arn:aws:secretsmanager:us-east-1:12345678910:secret:SecretOne-??????”,
               "arn:aws:secretsmanager:us-east-1:12345678910:secret:SecretTwo-??????",
               "arn:aws:secretsmanager:us-east-1:12345678910:secret:SecretThree-??????"
       }
   ]
}

We can attach the above policy to a lambda execution role for example to retrieve the 3 secrets in our resource array through a single API call. 

If you’re using AWS CDK and TypeScript the following example code might be useful.

We can create the secret resources, and grant the read permissions to our lambda. This will provide the GetSecretValue permissions we need, but we’ll need to define the BatchGetSecretValue and ListSecrets permissions ourselves.


const secretOne = new Secret(this, 'SecretOne', { secretName: 'SecretOne'});
const secretTwo = new Secret(this, 'SecretTwo', { secretName: 'SecretTwo'});
const secretThree = new Secret(this, 'SecretThree', { secretName: 'SecretThree'});

const lambda = new NodejsFunction(this, 'BatchGetSecretValueLambda', {
 functionName: 'batch-get-secret-value',
 runtime: Runtime.NODEJS_LATEST,
 handler: 'handler',
 entry: path.join(__dirname, '../src/batch-get-secret-value-lambda.ts'),
})

lambda.addToRolePolicy(
 new PolicyStatement({
   actions: [ 'secretsmanager:BatchGetSecretValue', 'secretsmanager:ListSecrets'],
   effect: Effect.ALLOW,
   resources: ["*"],
 }),
)

secretOne.grantRead(lambda)
secretTwo.grantRead(lambda)
secretThree.grantRead(lambda)

The code for the referenced NodeJS lambda, which will get the secrets in a single API call might look something like the following:


const client = new SecretsManagerClient()

const input = {
  "SecretIdList": [
    "SecretOne",
    "SecretTwo",
    "SecretThree"
  ]
};

try {
  const command = new BatchGetSecretValueCommand(input);
  const response = await client.send(command);

  return {
    statusCode: 200,
    body: JSON.stringify(response),
  }
} catch (err) {
  console.error(
    'Unable to get secrets. Error JSON:',
    JSON.stringify(err, null, 2),
  )
  return {
    statusCode: 400,
    body: 'Bad Request!',
  }
}

The above is for illustrative purposes only, you likely don’t want to be returning secrets from the lambda, but will utilise them within the lambda session.

In conclusion the BatchGetSecretValue action does not provide batch access to your secrets, but it does provide the ability to get your secrets using a batch request. The access to your secrets is still controlled by GetSecretValue.

Useful Resources