AWS Boto3: See Permissions Required to Run Code Successfully - amazon-web-services

Let's say I have a lambda function on AWS running some boto3 code. This boto3 code interacts with a variety of AWS resources, such as CloudWatch, S3, SNS, Lambda etc. In the execution role, I obviously may need to add certain permissions, such as lambda::CreateFunction as an example.
Now I want to add a permission policy to this function and add all the necessary permissions. Currently the only way to do this seems to be to run the code, read the error about it not having access to a certain permission, and then adding that permission to the permission policy. This can get very tedious and time consuming, especially when the code interacts with a large variety of different AWS resources.
So my question is, are there any ways to just see what permissions the boto3 code will require before running it? Maybe somebody has made a script for this before that reads through the code and prints out the permissions that would be necessary to run it?

If you build your services using the AWS Cloud Development Kit (CDK), then you can simplify the process of granting your Lambda permissions to the correct services.
As a simple example, say you have a Lambda function and a DynamoDB table. You design the infrastructure in CDK with something like this:
const someTable = new dynamodb.Table(this, 'someTable', {
tableName: 'someTable',
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
partitionKey: {name: 'id', type: dynamodb.AttributeType.STRING},
});
const someLambdaFunction = new lambda.Function(this, 'someLambdaFunction', {
functionName: 'someLambdaFunction',
runtime: lambda.Runtime.NODEJS_16_X,
handler: 'index.handler',
memorySize: 128,
timeout: cdk.Duration.seconds(10),
code: lambda.Code.fromAsset(path.join(__dirname, '../yourpath')),
});
And then you can grant permissions in one line:
someTable.grantFullAccess(someLambdaFunction);
CDK then generates the CloudFormation templates with the correct roles and permissions.

Related

Give AWS Lambda an AWS Managed Policy with CDK

I have a Lambda function defined in CDK. I'm using this Lambda to invoke a State Machine and for that I would need to provide it some Policies. The way I tried was the following:
const stepFunctionsPolicy = new PolicyStatement({
effect: Effect.ALLOW,
actions: ["states:*"],
resources: ['*']
})
MachineLambda.addToRolePolicy(stepFunctionsPolicy) //Added the Policy to the Lambda's Role
This is a workaround, but ideally, I would like to provide AWS Managed Policies, instead of manually defining each policy, to this Lambda function (specifically the AWSStepFunctionsFullAccess)?
The question specifically asks how to add the AWSStepFunctionsFullAccess managed policy to the Lambda's role. This allows the Lambda to perform CRUD operations on all step functions:
machineLambda.role?.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AWSStepFunctionsFullAccess")
);
Consider granting the Lambda narrow permissions instead, following the IAM least privilege permissions security best practice:
myStateMachine.grantExecution(machineLambda);

CDK Codepipeline CloudFormationCreateUpdateStackAction getting "S3: Access Denied" only with Nested Stacks

I am trying to set up a CDK Codepipeline for updating the cdk project itself, with the project being under one stack, and having multiple nested stacks in the constructor. The pipeline is in a second stack with the service stack passed in to access the name. I am using CloudFormationCreateUpdateStackAction to update the stack after I have run cdk synth and put the output in an artifact using codebuild.
pipeline.addStage({
stageName: 'ServiceUpdate',
actions: [
new CloudFormationCreateUpdateStackAction({
actionName: 'Service_Update',
stackName: props.serviceStack.stackName,
templatePath: cdkPipelineBuildOutput.atPath(
`${props.serviceStack.stackName}.template.json`
),
adminPermissions: true,
}),
],
});
This is able to update the stack if it is empty, or has some resources directly in it, however if there is a nested stack inside the service stack I get
S3: AccessDenied
for each of the nested stacks inside of the stack.
If I run "cdk deploy ExampleServiceStackName" from my terminal with admin credentials the nested stacks are created/updated correctly, leading me to believe that there is something wrong with the IAM roles of codebuild or codepipeline here. But I don't know where to start as I have set adminPermissions to true in the CloudFormationCreateUpdateStackAction.
I also manually set admin permissions by calling addToDeploymentRolePolicy on the CloudFormationCreateUpdateStackAction, and CodePipeline, passing
const policy = new PolicyStatement({
resources: ['*'],
actions: ['*'],
effect: Effect.ALLOW,
});
with no change in the access denied error.
I also make sure to specify "cdk synth --all" in my ci script in an attempt to ensure the nested stacks templates will be synthesized.
Other stack overflow questions I have read:
S3 error: Access Denied when deploying CFN template with Nested Stacks
This Q was related to a typo in the manually written cloud formation template. I have looked in the generated templates, and the nested stack name is correctly generated and referenced by cdk. cdk deploy from local terminal also works, further leading me to believe there is no typo problem. I also pass the service stack as a prop and call the stackName property to avoid a typo in accessing the template.
If you spot a way there could be a problem accessing due to a typo, please let me know as that would still be the best-case scenario.
Codepipeline S3 Bucket access denied in Codebuild
This Q says it was solved by giving permissions to the CMK on the S3 bucket. I have used a code pipeline Artifact for source of the "cdk synth -> cloudformation templates". I'm not aware of any KMS CMK being used in this setup. If there is a way I can specify decryption abilities on the artifact maybe that would help.
If there is a way to get more verbose error messages about the s3: Access Denied status that would also be appreciated. It doesn't even share what s3 bucket is being denied, I'm just having to assume.
Thanks for any suggestions.

AWS CloudFormation: Cognito LambdaTrigger CustomEmailSender - Property "Not currently supported by AWS CloudFormation." and CDK usage

Generally what means Property "Not currently supported by AWS CloudFormation" for a CDK implementation, specifically:
In the CloudFormation Properties for the Cognito Userpool Lambda Config it says:
CustomEmailSender - Not currently supported by AWS CloudFormation.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-lambdaconfig.html
In the CDK for Cognito.CfnUssrPool this property is described:
https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-cognito.CfnUserPool.LambdaConfigProperty.html#customemailsender
My question now is whether this can be implemented with CDK at all? Currently, our Cognito is provided completely via CDK and I would like to keep it that way.
Edit:
I found a link (Using CustomEmailSender with CFN) where it says that contrary to the documentation it does seem to work and only the documentation has not been updated, I will test this and give feedback.
After testing CustomEmailSender CDK implementation, I have to say that the AWS CloudFormation Documentation hasn't a current state. So it is possible to use this function by CFN and CDK. AWS Doc CFN Cognito CustomEmailSender
ToDos in CDK:
Configure Cognito: instead of using the property emailConfiguration you have to use lambdaConfig:
lambdaConfig: {
customEmailSender: { lambdaArn: customSenderEmailLambdaArn, lambdaVersion: 'V1_0' },
// the version is an ENUM so be careful when you set it
kmsKeyId: kmsKeyArn,
},
As you see here you have also to set up a KMS Key (you can adjust an existing one or create a new KMS key). The most important thing here is that you allow the action kms:CreateGrant to Cognito AND the Lambda function!
Another important ToDo is to add your Lambda the permission for Cognito, action cognito-idp:InvokeFunction.
The big advantage of using CustomEmailSender instead of the common Lambda Triggers that you don't have to set all the triggers in you CDK code or in the Cognito Console (all email events will be sent automatically to your lambda).

AWS Copilot - Attach Imported Policy to Task Role

I'm trying to use CloudFormation AddOn template in the following scenario:
Service 1
creates an SNS Topic and a Managed Policy that has all the necessary permissions to publish to it. The SNS Topic will collect "Activity" records and then fan them out to multiple subscribers.
A common code library abstracts away the usage of SNS - any applications that need to post activity messages do so without any knowledge that SNS is being used underneath the covers.
Service N needs to publish activity messages using the common code library and needs whatever permissions are necessary.
So service 1 writes the Managed Policy ARN out as an exported output to the AddOn stack like so:
Outputs:
activityPublishPolicy:
Description: "Activity Publish Policy ARN"
Value: !Ref activitySnsTopicPublishPolicy
Export:
Name: !Sub ${App}-${Env}-activity-publish-policy
Then in service N, I was hoping to import the ARN of the publishing policy and get it attached to the task role:
Outputs:
activityPublishAccessPolicy:
Description: "The IAM::ManagedPolicy to attach to the task role."
Value: !ImportValue
'Fn::Sub': '${App}-${Env}-activity-publish-policy'
The ARN is imported just fine and written out to the Cloud Formation stack of Service N; however, the Task Role does not get the Managed Policy attached to it.
I did a quick test to see if adding the policy directly to the AddOn stack would attach and that does indeed work.
Outputs:
activityPublishAccessPolicy:
Description: "The IAM::ManagedPolicy to attach to the task role."
Value: !Ref activityPolicy
This leads me to believe that Copilot only attaches ManagedPolicies to the Task Role that are created in its own AddOn Stack, but that's just a guess.
I'd prefer not to write a new policy in every service to do this, and I'd prefer not to open up the topic policy our whole VPC if possible.
Is there a better way of doing this?
Thanks!
This is because Copilot scans the Addons template to determine the type of the resource you're outputting. There are several "magic" outputs for addons. They are:
Security Groups
Managed Policies
Secrets
To detect these outputs, we scan the template looking for the logical ID of the referenced resource. This means that we don't currently have a way of deriving the resource type of the results of Fn::ImportValue calls, since they don't refer to a logical ID defined in that addons template!
I'm sorry this is causing you problems--it seems like you may need to add the managed policy to the addons stack of each service you want to grant this access to. This is something we might be able to do something about, though, and would love if if you could cut us a Github issue so we can prioritize and gather feedback on a proposal.

How to avoid giving `iam:CreateRole` permission when using existing S3 bucket to trigger Lambda function?

I am trying to deploy an AWS Lambda function that gets triggered when an AVRO file is written to an existing S3 bucket.
My serverless.yml configuration is as follows:
service: braze-lambdas
provider:
name: aws
runtime: python3.7
region: us-west-1
role: arn:aws:iam::<account_id>:role/<role_name>
stage: dev
deploymentBucket:
name: serverless-framework-dev-us-west-1
serverSideEncryption: AES256
functions:
hello:
handler: handler.hello
events:
- s3:
bucket: <company>-dev-ec2-us-west-2
existing: true
events: s3:ObjectCreated:*
rules:
- prefix: gaurav/lambdas/123/
- suffix: .avro
When I run serverless deploy, I get the following error:
ServerlessError: An error occurred: IamRoleCustomResourcesLambdaExecution - API: iam:CreateRole User: arn:aws:sts::<account_id>:assumed-role/serverless-framework-dev/jenkins_braze_lambdas_deploy is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::<account_id>:role/braze-lambdas-dev-IamRoleCustomResourcesLambdaExec-1M5QQI6P2ZYUH.
I see some mentions of Serverless needing iam:CreateRole because of how CloudFormation works but can anyone confirm if that is the only solution if I want to use existing: true? Is there another way around it except using the old Serverless plugin that was used prior to the framework adding support for the existing: true configuration?
Also, what is 1M5QQI6P2ZYUH in arn:aws:iam::<account_id>:role/braze-lambdas-dev-IamRoleCustomResourcesLambdaExec-1M5QQI6P2ZYUH? Is it a random identifier? Does this mean that Serverless will try to create a new IAM role every time I try to deploy the Lambda function?
I've just encountered this, and overcome it.
I also have a lambda for which I want to attach an s3 event to an already existing bucket.
My place of work has recently tightened up AWS Account Security by the use of Permission Boundaries.
So i've encountered the very similar error during deployment
Serverless Error ---------------------------------------
An error occurred: IamRoleCustomResourcesLambdaExecution - API: iam:CreateRole User: arn:aws:sts::XXXXXXXXXXXX:assumed-role/xx-crossaccount-xx/aws-sdk-js-1600789080576 is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::XXXXXXXXXXXX:role/my-existing-bucket-IamRoleCustomResourcesLambdaExec-LS075CH394GN.
If you read Using existing buckets on the serverless site, it says
NOTE: Using the existing config will add an additional Lambda function and IAM Role to your stack. The Lambda function backs-up the Custom S3 Resource which is used to support existing S3 buckets.
In my case I needed to further customise this extra role that serverless creates so that it is also assigned the permission boundary my employer has defined should exist on all roles. This happens in the resources: section.
If your employer is using permission boundaries you'll obviously need to know the correct ARN to use
resources:
Resources:
IamRoleCustomResourcesLambdaExecution:
Type: AWS::IAM::Role
Properties:
PermissionsBoundary: arn:aws:iam::XXXXXXXXXXXX:policy/xxxxxxxxxxxx-global-boundary
Some info on the serverless Resources config
Have a look at your own serverless.yaml, you may already have a permission boundary defined in the provider section. If so you'll find it under rolePermissionsBoundary, this was added in I think version 1.64 of serverless
provider:
rolePermissionsBoundary: arn:aws:iam::XXXXXXXXXXXX:policy/xxxxxxxxxxxx-global-boundary
If so, you can should be able to use that ARN in the resources: sample I've posted here.
For testing purpose we can use:
provider:
name: aws
runtime: python3.8
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action: "*"
Resource: "*"
For running sls deploy, I would suggest you use a role/user/policy with Administrator privileges.
If you're restricted due to your InfoSec team or the like, then I suggest you have your InfoSec team have a look at docs for "AWS IAM Permission Requirements for Serverless Framework Deploy." Here's a good link discussing it: https://github.com/serverless/serverless/issues/1439. At the very least, they should add iam:CreateRole and that can get you unblocked for today.
Now I will address your individual questions:
can anyone confirm if that is the only solution if I want to use existing: true
Apples and oranges. Your S3 configuration has nothing to do with your error message. iam:CreateRole must be added to the policy of whatever/whoever is doing sls deploy.
Also, what is 1M5QQI6P2ZYUH in arn:aws:iam::<account_id>:role/braze-lambdas-dev-IamRoleCustomResourcesLambdaExec-1M5QQI6P2ZYUH? Is it a random identifier? Does this mean that serverless will try to create a new role every time I try to deploy the function?
Yes, it is a random identifier
No, sls will not create a new role every time. This unique ID is cached and re-used for updates to an existing stack.
If a stack is destroyed/recreated, it will assign a generate a new unique ID.