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

Viewed 893

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 Answers

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.

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.

Related