Joey Yi Zhao
Joey Yi Zhao

Reputation: 42500

How to grant lambda permission to upload file to s3 bucket in `terraform`?

I have below lambda function configuration in TerraForm:

resource "aws_lambda_function" "test_lambda" {
  # filename         = "crawler/dist/deploy.zip"
  s3_bucket = "${var.s3-bucket}"
  s3_key    = "${aws_s3_bucket_object.file_upload.key}"
  # source_code_hash = "${filebase64sha256("file.zip")}"
  function_name    = "quote-crawler"
  role             = "arn:aws:iam::773592622512:role/LambdaRole"
  handler          = "handler.handler"
  source_code_hash = "${data.archive_file.zipit.output_base64sha256}"
  runtime          = "${var.runtime}"
  timeout          = 180

  environment {
    variables = {
      foo = "bar"
    }
  }
}

when I run the lambda I got the error "errorMessage": "An error occurred (AccessDenied) when calling the PutObject operation: Access Denied", when it tries to upload file to s3 bucket. It seems that the lambda function doesn't have permission to access s3. TerraForm doc is not clear about how to configure them. The permission configuration panel doesn't appear on lambda console either. It seems that lambda that created by TerraForm has limited configuration for me to use. So how can I grant s3 permission to lambda?

Upvotes: 8

Views: 15821

Answers (3)

mellifluous
mellifluous

Reputation: 2975

I would do it in the following order:

this code is using terraform 0.12.*

  1. Create policy documents for assume role and s3 permissions
data aws_iam_policy_document lambda_assume_role {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

data aws_iam_policy_document lambda_s3 {
  statement {
    actions = [
      "s3:PutObject",
      "s3:PutObjectAcl"
    ]

    resources = [
      "arn:aws:s3:::bucket/*"
    ]
  }
}
  1. Create an IAM policy
resource aws_iam_policy lambda_s3 {
  name        = "lambda-s3-permissions"
  description = "Contains S3 put permission for lambda"
  policy      = data.aws_iam_policy_document.lambda_s3.json
}
  1. Create a role
resource aws_iam_role lambda_role {
  name               = "lambda-role"
  assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
  1. Attach policy to role
resource aws_iam_role_policy_attachment lambda_s3 {
  role       = aws_iam_role.lambda_role.name
  policy_arn = aws_iam_policy.lambda_s3.arn
}
  1. Attach role to lambda
resource "aws_lambda_function" "test_lambda" {
  # filename         = "crawler/dist/deploy.zip"
  s3_bucket = var.s3-bucket
  s3_key    = aws_s3_bucket_object.file_upload.key
  # source_code_hash = "${filebase64sha256("file.zip")}"
  function_name    = "quote-crawler"
  role             = aws_iam_role.lambda_role.arn
  handler          = "handler.handler"
  source_code_hash = data.archive_file.zipit.output_base64sha256
  runtime          = var.runtime
  timeout          = 180

  environment {
    variables = {
      foo = "bar"
    }
  }
}

Upvotes: 3

Adiii
Adiii

Reputation: 59956

To make it easy you can do this in three steps,

  1. create a role
  2. create policy
  3. attached policy to the role
  4. attached role to lambda

Create role.

resource "aws_iam_role" "role" {
  name = "${var.env_prefix_name}-alb-logs-to-elk"
  path = "/"

      assume_role_policy = <<EOF
    {

  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

Create a policy that has specified access to s3

 #Created Policy for IAM Role
resource "aws_iam_policy" "policy" {
  name = "${var.env_prefix_name}-test-policy"
  description = "A test policy"


      policy = <<EOF
   {
"Version": "2012-10-17",
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "logs:*"
        ],
        "Resource": "arn:aws:logs:*:*:*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "s3:*"
        ],
        "Resource": "arn:aws:s3:::*"
    }
]

} 
    EOF
    }

Attached IAM Role and the new created Policy

resource "aws_iam_role_policy_attachment" "test-attach" {
  role       = "${aws_iam_role.role.name}"
  policy_arn = "${aws_iam_policy.policy.arn}"
}

Now attached the role to Lamba source

resource "aws_lambda_function" "test_lambda" {
  # filename         = "crawler/dist/deploy.zip"
  s3_bucket = "${var.s3-bucket}"
  s3_key    = "${aws_s3_bucket_object.file_upload.key}"
  # source_code_hash = "${filebase64sha256("file.zip")}"
  function_name    = "quote-crawler"
  role             = "${aws_iam_role.role.arn}"
  handler          = "handler.handler"
  source_code_hash = "${data.archive_file.zipit.output_base64sha256}"
  runtime          = "${var.runtime}"
  timeout          = 180

  environment {
    variables = {
      foo = "bar"
    }
  }
}

Upvotes: 11

Julien Simon
Julien Simon

Reputation: 2729

The IAM role associated to the function is not allowed to upload to S3.

The solution is to create an IAM policy allowing S3 access to your bucket (say read/write), which would look something like:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ListObjectsInBucket",
            "Effect": "Allow",
            "Action": ["s3:ListBucket"],
            "Resource": ["arn:aws:s3:::bucket-name"]
        },
        {
            "Sid": "AllObjectActions",
            "Effect": "Allow",
            "Action": "s3:*Object",
            "Resource": ["arn:aws:s3:::bucket-name/*"]
        }
    ]
}

Then, you need to attach this policy to the role used by your lambda function.

More info at: https://www.terraform.io/docs/providers/aws/r/iam_role_policy.html

Upvotes: 6

Related Questions