简体   繁体   中英

Terraform - How to create IAM role for AWS Lambda and deploy both?

I am learning Terraform. I am trying to create a new Lambda function. And I realized that I also need to create an IAM role. So I am trying to do both using Terraform. But it does not allow me to create the role.

This is my Terraform file

provider "aws" {
  profile = "default"
  region  = "eu-west-1"
}

data "aws_iam_policy" "AWSLambdaBasicExecutionRole" {
  arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_iam_role" "terraform_function_role" {
  name               = "terraform_function_role"
  assume_role_policy = "${data.aws_iam_policy.AWSLambdaBasicExecutionRole.policy}"
}

resource "aws_lambda_function" "terraform_function" {
  filename         = "terraform_function.zip"
  function_name    = "terraform_function"
  handler          = "index.handler"
  role             = "${aws_iam_role.terraform_function_role.id}"
  runtime          = "nodejs8.10"
  source_code_hash = "${filebase64sha256("terraform_function.zip")}"
}

This is the error that I am getting

Error creating IAM Role terraform_function_role: MalformedPolicyDocument: Has prohibited field Resource
status code: 400

How do I fix this?

Since you are still in the learning phase, I suggest you move to terraform 0.12 instead, so you can use things like templatefile . therefore you don't need to create data objects.

One other thing is to always use the Least Privilege Principle when creating policies, meaning your Resource (Lambda, on this case) will only have access to what it needs. For now, it's only CloudWatch, but in a real world scenario this is very likely not the case.

Back to your question, here's how you can create an IAM Role, an IAM Policy and finally an IAM Policy Attachment (this is the bridge between the policy and the role) as well as the AssumeRolePolicy (this is the trust relationship between the service it's going to use it and the role itself). I have extracted it all into templates for you so it's easier to maintain later on. The gist (for an easier read on the eyes) can be found here .

# iam_role
resource "aws_iam_role" "iam_role" {
  name = "iam-role"

  assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
}

#content of lambda-base-policy.tpl
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}

#iam_policy
resource "aws_iam_policy" "policy" {
  name = "policy"

  policy = templatefile("${path.module}/templates/cloudwatch-policy.tpl", {})
}

#content of cloudwatch-policy.tpl
{
  "Version": "2012-10-17",
  "Statement": [
    {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
  ]
} 

#iam_policy_attachment
resource "aws_iam_policy_attachment" "policy_attachment" {
  name       = "attachment"
  roles      = ["${aws_iam_role.iam_role.name}"]
  policy_arn = "${aws_iam_policy.policy.arn}"
} 

As mentioned in the comment, you have to create assume role then attach the assume rule with your newly created policy, here is the complete working example.

#assume role
resource "aws_iam_role" "role" {
      name = "test-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
    }

# Created Policy for IAM Role (s3 and log access)
resource "aws_iam_policy" "policy" {
  name = "my-test-policy"
  description = "A test policy"


  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
          "s3:Get*",
          "s3:List*"
          ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
  ]
}
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}"
}



# Created AWS Lamdba Function: Memory Size, NodeJS version, handler, endpoint, doctype and environment settings
resource "aws_lambda_function" "elb_logs_to_elasticsearch" {
  function_name = "mytest-alb-logs-to-elk"
  description   = "elb-logs-to-elasticsearch"
  memory_size = 1024
  filename         = "terraform_function.zip"
  runtime          = "nodejs8.10"
  role             = "${aws_iam_role.role.arn}"
  handler          = "index.handler"


}

IAM Role's trust relationship (or assume role policy) defines which resource / service can assume the role. In this, we don't define the Resource field. Hence we can't attach IAM policies or use that policy as is. The correct format for Trust Relationship is:

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

In this scenario, all Lambda functions in your account can assume this role.

You can refer this AWS link for more examples.

Edit: Based on @ydaetskcoR comment, here's a working example:

provider "aws" {
  profile = "default"
  region  = "eu-west-1"
}

data "aws_iam_policy_document" "AWSLambdaTrustPolicy" {
  statement {
    actions    = ["sts:AssumeRole"]
    effect     = "Allow"
    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "terraform_function_role" {
  name               = "terraform_function_role"
  assume_role_policy = "${data.aws_iam_policy_document.AWSLambdaTrustPolicy.json}"
}

resource "aws_iam_role_policy_attachment" "terraform_lambda_policy" {
  role       = "${aws_iam_role.terraform_function_role.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_lambda_function" "terraform_function" {
  filename         = "terraform_function.zip"
  function_name    = "terraform_function"
  handler          = "index.handler"
  role             = "${aws_iam_role.terraform_function_role.arn}"
  runtime          = "nodejs8.10"
  source_code_hash = "${filebase64sha256("terraform_function.zip")}"
}

The changes from your code include the following:

  • Updated aws_iam_policy_document resource for assume role permissions
  • Changed aws_iam_role resource to use the above mentioned policy document
  • Created aws_iam_role_policy_attachment to attach LambdaBasicExecution policy (which enables logging to CloudWatch)
  • Updated aws_lambda_function resource to use IAM Role's ARN instead of Id because Lambda function needs the ARN

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM