3
votes

I am trying to create some service users with specific roles. I don't want to hardcode the policy documents for my service role.

Example:

service-1 required read access to -EC2 instance, Cloudwatch logs and write access to s3 bucket.

Service-2 needs SNS, SQS, and DynamoDB.

Provider=AWS

Any help will be much appreciated.

How do I create a new policy on runtime and attach it to a role without hardcoding the policy_document JSON in code resource?

2
This is a good question because even i was stuck on it for a while. This may help somebody.Shubham Singh

2 Answers

1
votes

Apart from your question, there is a great document to read about HCL Language. https://github.com/hashicorp/hcl

Now as far as I understand your question, it seems that you want to create a permission module and be able to pass it service-name and permission. That specific method will allow you to reuse the IAM role creation code. I suggest you read https://www.terraform.io/docs/configuration/modules.html.

You can create a Module like IAM Permission and do something like

   resource "aws_iam_policy" "policy" {
 name        = "${var.service_name}"
 description = "${var.service_name} access policy"
 policy      = templatefile("${path.module}/ddb_role.tpl", {
   role            = var.role
 })
}

resource "aws_iam_role" "role" {
 name               = "role_${var.env}"
 assume_role_policy = "${file("assumerolelambdapolicy.json")}"
}

resource "aws_iam_role_policy_attachment" "policy_attachment" {
  role    = "${aws_iam_role.role.name}"
  policy_arn = "${aws_iam_policy.policy.arn}"
} 

Template file:

{
    "Version": "2012-10-17",
    "Statement": [

     {
        "Effect": "Allow",
        "Action": ${role},
        "Resource": "*"
      }
  ]
}

var.tf you could store a list of permissions

variable "role" {
    type = list
    default = [
 "sqs:*",
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "ec2:Describe*",
          "ec2:DescribeSecurityGroups",
          "ec2:DescribeSubnets",
          "ec2:DescribeVpcs",
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "ec2:CreateNetworkInterface",
          "ec2:DescribeNetworkInterfaces",
          "ec2:DeleteNetworkInterface"
]
}

This is something you should be able to override using tfvar file. Try this and let me know.

1
votes

Terraform modules have variables for the parts that vary. Things that don't need to vary should be hardcoded as a best practice.

Otherwise you will be creating modules that do nothing and take code as input.

Any sufficiently complicated [dynamic Terraform module] contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of [Terraform itself]..

remix of Greenspun's tenth rule

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    sid    = "AllowAssumeRole"
    effect = "Allow"
    actions = [
      "sts:AssumeRole",
    ]
    resources = var.trusted_arns
  }
}

You can grant access to a set of AWS buckets as a variable like this:

  statement {
    sid    = "AllowS3Access"
    effect = "Allow"
    actions = [
      "s3:GetBucketLocation",
      "s3:ListBucket",
      "s3:ListBucketMultipartUploads",
      "s3:GetObject",
      "s3:ListMultipartUploadParts",
      "s3:AbortMultipartUpload",
    ]
    resources = var.allowed_read_only_s3_bucket_arns
  }

If you are bent on making dynamic policies you can do this:

data "aws_iam_policy_document" "meta_policy" {
  dynamic "statement" {
    for_each = var.statements
    content { 
      sid       = each.key
      effect    = each.value.effect
      actions   = each.value.actions
      resources = each.value.resources
    }
  }
}

Would be used with a variable like this:

variable "statements" {
  description = "I didn't want to use resources, so I made meta-Terraform"
  type        = map(object(
    {
      sid       = string
      effect    = string
      actions   = list(string)
      resources = list(string)
    }
  ))
}

Someone looking at such a module would have no way of knowing what it needs and would be relying purely on input variables. Maybe this is suitable for your use case, but I recommend against it.

Your colleagues, including your future self will thank you if you embrace hardcoding things that don't actually change.