5

I have created a Lambda Python function through AWS Cloud 9 but have hit an issue when trying to write to an S3 bucket from the Lambda Function. When I test in Cloud 9 the Python codes runs fine and writes to the S3 bucket perfectly. When I push this to the Lambda function and it runs I think get the error. This makes me think there is a permission different between the roles used to run the application in Could 9, and that when the Lambda function runs.

The below error is given and I am looking for some advise on what I could be missing, below the error I have described the setup:

[ERROR] ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
Traceback (most recent call last):
  File "/var/task/index.py", line 22, in handler
    s3.Bucket(bucket_name).put_object(Key=s3_path, Body=encoded_string)
  File "/var/runtime/boto3/resources/factory.py", line 520, in do_action
    response = action(self, *args, **kwargs)
  File "/var/runtime/boto3/resources/action.py", line 83, in __call__
    response = getattr(parent.meta.client, operation_name)(**params)
  File "/var/runtime/botocore/client.py", line 320, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/var/runtime/botocore/client.py", line 623, in _make_api_call
    raise error_class(parsed_response, operation_name)

The code I have is as follows:

    import json
    import datetime
    from botocore.vendored import requests
    import boto3


    def handler(event, context):
        print("Start:")
        response = requests.get('https://##########')

        data = response.json()

        for i in data:
            print (i)
            encoded_string = json.dumps(i).encode("utf-8")
            bucket_name = "data"
            file_name = str(i['id']) + ".txt"
            lambda_path = "/tmp/" + file_name
            s3_path = "testBucket/" + file_name
            s3 = boto3.resource("s3")
            s3.Bucket(bucket_name).put_object(Key=s3_path, Body=encoded_string)
...rest of code ...

The .yml file with the necessary permissions is as follows:

AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar

Parameters:
  ProjectId:
    Type: String
    Description: CodeStar projectId used to associate new resources to team members
  CodeDeployRole:
    Type: String
    Description: IAM role to allow AWS CodeDeploy to manage deployment of AWS Lambda functions
  Stage:
    Type: String
    Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed.
    Default: ''

Globals:
  Function:
    AutoPublishAlias: live
    DeploymentPreference:
      Enabled: true
      Type: Canary10Percent5Minutes
      Role: !Ref CodeDeployRole

Resources:
  HelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: python3.7
      Timeout: 10
      Role:
        Fn::GetAtt:
        - LambdaExecutionRole
        - Arn
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /
            Method: get
        PostEvent:
          Type: Api
          Properties:
            Path: /
            Method: post
  LambdaExecutionRole:
    Description: Creating service role in IAM for AWS Lambda
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}'
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [lambda.amazonaws.com]
          Action: sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        -  arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/CodeStar_${ProjectId}_PermissionsBoundary'

The role the Lambda function executes with is as follows: IAM Role

Any advice as to where this could be going wrong. I understand that I need to provide the correct access but I am unsure as to where else I need to specify the correct access (I really do not want to make my S3 public just so my Lambda function can access it). In Lambda it shows that S3 has been added as a resource the functions role has access to but yet receiving the above error.

MLu
  • 23,798
  • 5
  • 54
  • 81
W. Walford
  • 53
  • 1
  • 5

1 Answers1

3

I believe the solution should be as simple as changing your LambdaExecutionRole to this:

LambdaExecutionRole:
  Description: Creating service role in IAM for AWS Lambda
  Type: AWS::IAM::Role
  Properties:
    RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}'
    AssumeRolePolicyDocument:
      Statement:
      - Effect: Allow
        Principal:
          Service: [lambda.amazonaws.com]
        Action: sts:AssumeRole
    Path: /
    ManagedPolicyArns:
    - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    - arn:aws:iam::aws:policy/AmazonS3FullAccess         # <== Add
    # PermissionsBoundary: !Sub ...                      # <== Comment out

If this works you can then experiment with restricting S3 permissions to a particular bucket but for start try to add the AmazonS3FullAccess policy and comment out PermissionsBoundary.

Hope that helps :)

MLu
  • 23,798
  • 5
  • 54
  • 81
  • Thanks - after posting this and digging a bit deeper I found that the permissions boundary did not include S3 and therefore was blocking this [Link to AWS Docs] (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) . What is the advantage of using PermissionBoundary's, the above seems to suggest removing the PersmissionBoundary completely and only use the Role permissions. – W. Walford Feb 18 '19 at 23:40
  • 1
    @W.Walford the *Permission Boundary* is like a 2nd line of defence. If you accidentally open something you didn't want to in the *Policy* the *Permission Boundary* can still stop it. It's more complex to manage because a new permission must be added in two places but a good practice in production environments. On the other hand when debugging some problem it's best to simplify it as much as possible, hence I suggested removing (temporarily) the Permission Boundary from the Policy. – MLu Feb 19 '19 at 00:00