Aws-sam-cli: document deploy permissions?

Created on 18 May 2018  Â·  16Comments  Â·  Source: aws/aws-sam-cli

It would be really helpful if there were a documented set of permissions necessary for sam package and sam deploy to work. We're slowly adding permissions to our deploy role until we get a success.

aredeploy arepackage priorit3-long-term typdocumentation

Most helpful comment

FWIW, here's what I used (as part of a role definition in my SAM template) to get this working today (it was a pain to iterate to get to this, so hopefully it'll help someone here). See notes below.

    AdditionalPermissionPolicy:
        Type: "AWS::IAM::Policy"
        Properties:
            PolicyName: "root"
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:PutObject"
                            - "s3:GetObject"
                            - "s3:CreateMultipartUpload"
                        Resource:
                            - "arn:aws:s3:::{BUCKET_NAME}"
                            - "arn:aws:s3:::{BUCKET_NAME}/*"

                    -
                        Effect: "Allow"
                        Action:
                            - "cloudformation:DescribeStacks"
                            - "cloudformation:CreateChangeSet"
                            - "cloudformation:ExecuteChangeSet"
                            - "cloudformation:DescribeChangeSet"
                        Resource:
                            - !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/{STACK_NAME}/*"
                            - "arn:aws:cloudformation:us-east-1:aws:transform/Serverless-2016-10-31"

                    -
                        Effect: "Allow"
                        Action:
                            - "cloudformation:GetTemplateSummary"
                        Resource: "*"

                    -
                        Effect: "Allow"
                        Action:
                            - "iam:GetRole"
                        Resource:
                            - !Sub "arn:aws:iam::${AWS::AccountId}:role/{STACK_NAME}-{FUNCTION_NAME}Role-*"

                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:UpdateFunctionCode"
                            - "lambda:ListTags"
                            - "lambda:TagResource"
                            - "lambda:UntagResource"
                            - "lambda:GetFunctionConfiguration"
                        Resource:
                            - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:{STACK_NAME}-{FUNCTION_NAME}-*"

Notes:

  • this is for (code) updates only; I can't imagine that it would work for deploying a new stack
  • replace the following:

    • {BUCKET_NAME} with the bucket name you're using for code upload

    • {STACK_NAME} with your stack name (see below)

    • {FUNCTION_NAME} with your function name; this is FirstFunction by default in the python cookiecutter template

  • I use this in a secondary stack that sets up a role for Codebuild, which is why I used {STACK_NAME} instead of ${AWS::StackName}, but you can probably use that

All 16 comments

+1

Due to the complex security structure of AWS (which I personally am a fan of), this becomes a bottleneck when trying to adopt SAM in our development teams. Our development roles are defined by security, and it would be far easier to work with them with a preexisting list of permissions needed rather than having to go back-and-forth.

Any news on this?

FWIW, here's what I used (as part of a role definition in my SAM template) to get this working today (it was a pain to iterate to get to this, so hopefully it'll help someone here). See notes below.

    AdditionalPermissionPolicy:
        Type: "AWS::IAM::Policy"
        Properties:
            PolicyName: "root"
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:PutObject"
                            - "s3:GetObject"
                            - "s3:CreateMultipartUpload"
                        Resource:
                            - "arn:aws:s3:::{BUCKET_NAME}"
                            - "arn:aws:s3:::{BUCKET_NAME}/*"

                    -
                        Effect: "Allow"
                        Action:
                            - "cloudformation:DescribeStacks"
                            - "cloudformation:CreateChangeSet"
                            - "cloudformation:ExecuteChangeSet"
                            - "cloudformation:DescribeChangeSet"
                        Resource:
                            - !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/{STACK_NAME}/*"
                            - "arn:aws:cloudformation:us-east-1:aws:transform/Serverless-2016-10-31"

                    -
                        Effect: "Allow"
                        Action:
                            - "cloudformation:GetTemplateSummary"
                        Resource: "*"

                    -
                        Effect: "Allow"
                        Action:
                            - "iam:GetRole"
                        Resource:
                            - !Sub "arn:aws:iam::${AWS::AccountId}:role/{STACK_NAME}-{FUNCTION_NAME}Role-*"

                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:UpdateFunctionCode"
                            - "lambda:ListTags"
                            - "lambda:TagResource"
                            - "lambda:UntagResource"
                            - "lambda:GetFunctionConfiguration"
                        Resource:
                            - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:{STACK_NAME}-{FUNCTION_NAME}-*"

Notes:

  • this is for (code) updates only; I can't imagine that it would work for deploying a new stack
  • replace the following:

    • {BUCKET_NAME} with the bucket name you're using for code upload

    • {STACK_NAME} with your stack name (see below)

    • {FUNCTION_NAME} with your function name; this is FirstFunction by default in the python cookiecutter template

  • I use this in a secondary stack that sets up a role for Codebuild, which is why I used {STACK_NAME} instead of ${AWS::StackName}, but you can probably use that

+1 for this. Piecemealing through which roles to assign is becoming a grueling process and involves us going to the infosec team again and again for approvals. There's got to be a better way.

how on Earth is this NOT documented _anywhere_?!?!?! I've spent the entire day trying to figure out what permissions need to be added to even run the samples provided in this repo:

First, I couldn't create the bucket from the CLI, so I had to login and make a bucket and get those permissions correct....then this:

~/repos/github/my-project [master ?]> sam deploy --template-file serverless-output.yml --stack-name my-stack --capabilities CAPABILITY_IAM

An error occurred (AccessDenied) when calling the DescribeStacks operation: User: <my-user> is not authorized to perform: cloudformation:DescribeStacks on resource: <my-stack>

How on earth is anyone supposed to figure this all out. PLEASE....at least provided the details needed to even run the examples....

@scoates Awesome, awesome job. Thanks for tackling this before I spent my afternoon or longer on it.

I ended up having to add lambda:ListVersionsByFunction, lambda:UpdateAlias and lambda:PublishVersion for arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:[function_name] as well, in case that helps anyone.

Ohhh. This was annoying...

My set of permissions.

CIInstanceRole - a role that is attached to the EC2 instance where the CI is running. Must be assumed by the CI Job. Replace {ACCOUNT} with your number.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:DescribeStacks",
        "cloudformation:CreateChangeSet",
        "cloudformation:ExecuteChangeSet",
        "cloudformation:DescribeChangeSet"
      ],
      "Resource": [
        "arn:aws:cloudformation:us-east-1:aws:transform/Serverless-2016-10-31",
        "arn:aws:cloudformation:*:*:stack/*/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:GetTemplateSummary"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PassRoleToSAM",
      "Effect": "Allow",
      "Action": [
        "iam:PassRole"

      ],
      "Resource": "arn:aws:iam::{ACCOUNT}:role/CICloudFormationSharedDeploymentRole"
    }
  ]
}

CICloudFormationSharedDeploymentRole - a CI role that must be passed with --role-arn arn:aws:iam::{ACCOUNT}:role/CICloudFormationSharedDeploymentRole. Pay attention that iam:PassRole is configured for this resource in the role above.

This does not include API Gateway and DynamoDB permissions, add them if you need.
Replace {SAM_S3_BUCKET}, SAM_STACK_NAME with your number.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::{SAM_S3_BUCKET}",
        "arn:aws:s3:::{SAM_S3_BUCKET}/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:DescribeStacks",
        "cloudformation:CreateChangeSet",
        "cloudformation:ExecuteChangeSet",
        "cloudformation:DescribeChangeSet"
      ],
      "Resource": [
        "arn:aws:cloudformation:us-east-1:aws:transform/Serverless-2016-10-31",
        "arn:aws:cloudformation:*:*:stack/{SAM_STACK_NAME}/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:GetTemplateSummary"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:CreateRole",
        "iam:PassRole",
        "iam:DeleteRole",
        "iam:GetRolePolicy",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy",
        "iam:DetachRolePolicy",
        "iam:DeleteRolePolicy",
        "iam:TagRole",
        "iam:UntagRole"
      ],
      "Resource": [
        "arn:aws:iam::*:role/{SAM_STACK_NAME}-*"

      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "events:*"
      ],
      "Resource": [
        "arn:aws:events:*:*:rule/{SAM_STACK_NAME}-*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:*"
      ],
      "Resource": [
        "arn:aws:lambda:*:*:function:{SAM_STACK_NAME}-*"
      ]
    }
  ]
}

Some permissions raise questions, i.e "iam:PassRole" for role/{SAM_STACK_NAME}-*, but id didn't work without it. Also I had to add deleting permissions in case of the stack failure.

The role must also have Trust relationship configured as in the example so CI can pass the CICloudFormationSharedDeploymentRole to SAM:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::{ACCOUNT}:role/CIInstanceRole"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudformation.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

And the SAM command example:

sam deploy \
--region us-east-1 \
--stack-name {SAM_STACK_NAME} \
--s3-bucket {SAM_S3_BUCKET} \
--s3-prefix {SAM_STACK_NAME} \
--tags Creator=sam sam=Owned \
--template-file packaged.yaml \
--capabilities CAPABILITY_IAM \
--role-arn arn:aws:iam::{ACCOUNT}:role/CICloudFormationSharedDeploymentRole \
--no-fail-on-empty-changeset

Hey, some additional permissions that I found missing:

API Gateway:

Action = [
  "apigateway:GET",
  "apigateway:PATCH",
  "apigateway:POST",
  "apigateway:PUT"
]

Lambda:

Action = [
  "lambda:CreateFunction",
  "lambda:AddPermission"
]

Hello guys, I had to also add s3:ListBucket

Another one to allow: cloudformation:DescribeStackEvents, not to be confused with DescribeStacks

Does no one else have concerns over the need to pass this:

    {
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:CreateRole",
        "iam:PassRole",
        "iam:DeleteRole",
        "iam:GetRolePolicy",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy",
        "iam:DetachRolePolicy",
        "iam:DeleteRolePolicy",
        "iam:TagRole",
        "iam:UntagRole"
      ],
      "Resource": [
        "arn:aws:iam::*:role/{SAM_STACK_NAME}-*"

      ]
    },

To automate this would mean granting permissions to an entity to create a new role with any set of permissions in it, which is a bit of a no-go for me.

I suspect there might be a way around this with permissions boundaries, but for the life of me I haven't been able to figure it out as yet.

I had managed to avoid the issue of PutRolePolicy and AttachRolePolicy (just for function code deployments) prior to adding a PreTrafficHook, but after doing so they appear to be required, so I'm a bit stuck.

So for some context on those permissions, if you're creating IAM roles, you need to have the necessary permissions, and they are powerful permissions to have. If you want to get around this, there are two approaches:

  1. Create IAM roles elsewhere, and reference them by ARN in your SAM template. If you're doing this, you won't need permissions to create/delete roles (whatever account/user creates the roles needs those permissions, and this can happen outside of SAM template creation).
  2. For permissions boundaries, there are ways to ensure they are required to be present. For example, you could give a CloudFormation Deployment Role (or a user) those permissions, but only with a conditional:
          - Action:
              - iam:AttachRolePolicy
              - iam:CreateRole
              - iam:DeleteRolePolicy
              - iam:DetachRolePolicy
            Effect: Allow
            Resource: '*'
            Condition:
              StringEquals:
                iam:PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/my-app-${AWS::Region}-PermissionsBoundary'

That way, an IAM role can only be created if the permissions boundary you specify is included with it, and SAM provides a field to easily include this.

In any case, either of those workarounds should help you reduce the necessary permissions scope as you develop your applications further.

So far this policy is the smallest policy I managed to get sam deploy to release a new version of Lambda, YMMV! Not sure if it can be done with a smaller policy though ¯_(ツ)_/¯

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IAM",
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:PassRole"
      ],
      "Resource": [
        "arn:aws:iam::{ACCOUNT_ID}:role/{LAMBDA_ROLE}"
      ]
    },
    {
      "Sid": "S3",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::{SAM_S3_BUCKET}/*"
      ]
    },
    {
      "Sid": "Lambda",
      "Effect": "Allow",
      "Action": [
        "lambda:UpdateFunctionCode",
        "lambda:UpdateFunctionConfiguration",
        "lambda:TagResource",
        "lambda:ListTags",
        "lambda:GetFunctionConfiguration",
        "lambda:UntagResource"
      ],
      "Resource": [
        "arn:aws:lambda:*:{ACCOUNT_ID}:function:{LAMBDA}"
      ]
    },
    {
      "Sid": "CloudFormation",
      "Effect": "Allow",
      "Action": "cloudformation:*",
      "Resource": [
        "arn:aws:cloudformation:*:{ACCOUNT_ID}:stack/{LAMBDA_STACK}/*",
        "arn:aws:cloudformation:*:{ACCOUNT_ID}:stack/{SAM_MANAGED_STACK}/*",
        "arn:aws:cloudformation:us-west-2:aws:transform/Serverless-2016-10-31"
      ]
    }
  ]
}

If you use Lambda layers (to extract dependencies, etc):

          -
            Effect: "Allow"
            Action:
              - "lambda:PublishLayerVersion"
              - "lambda:GetLayerVersion"
              - "lambda:DeleteLayerVersion" # Required for Lambda upgrades
            Resource:
              - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:${LayerName}"
              - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:${LayerName}:*"

If you use 3rd party layers (like for monitoring systems):

          -
            Effect: "Allow"
            Action:
              - "lambda:GetLayerVersion"
            Resource:
              - !Sub "arn:aws:lambda:${AWS::Region}:464622532012:layer:Datadog-*:*"

To bind Lambda to some source like SQS queue (but usually it is required for first deploy only):

          -
            Action:
              - "lambda:CreateEventSourceMapping"
              - "lambda:GetEventSourceMapping"
            Effect: Allow
            Resource: '*'

That way, an IAM role can only be created if the permissions boundary you specify is included with it, and SAM provides a field to easily include this.

Damn, looks complicated! @awood45, can you please tell in more details how to do it? How permissions boundary should look like? I can't assemble this puzzle.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

goldenbearkin picture goldenbearkin  Â·  3Comments

drumadrian picture drumadrian  Â·  3Comments

PhungXuanAnh picture PhungXuanAnh  Â·  3Comments

rhlsthrm picture rhlsthrm  Â·  4Comments

joekiller picture joekiller  Â·  4Comments