1
votes

I'm trying to create data roles in three environments in AWS using Terraform. One is an role in root account. This role can is used to login to AWS and can assume data roles in production and staging. This works fine. This is using a separate module.

I have problems when trying to create the roles in prod and staging from a module. My module looks like this main.tf:

resource "aws_iam_role" "this" {

  name               = "${var.name}"
  description        = "${format("%s (managed by Terraform)", var.policy_description)}"
  assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}

resource "aws_iam_policy" "this" {
  name        = "${var.name}"
  description = "${format("%s (managed by Terraform)", var.policy_description)}"
  policy      = "${var.policy}"
}

data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${var.account_id}:root"]
    }
  }
}

data "aws_iam_policy_document" "assume_role_custom_principals" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type = "AWS"

      identifiers = [
        "${var.custom_principals}",
      ]
    }
  }
}

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

I also have the following in output.tf:

output "role_name" {
   value = "${aws_iam_role.this.name}"
}

Next I try to use the module to create two roles in prod and staging. main.tf:

module "data_role" {
  source = "../tf_data_role"

  account_id         = "${var.account_id}"
  name               = "data"
  policy_description = "Role for data engineers"

  custom_principals = [
    "arn:aws:iam::${var.master_account_id}:root",
  ]

  policy = "${data.aws_iam_policy_document.data_access.json}"
}

Then I'm trying to attach a AWS policies like this:

resource "aws_iam_role_policy_attachment" "data_readonly_access" {
  role       = "${module.data_role.role_name}"
  policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

resource "aws_iam_role_policy_attachment" "data_redshift_full_access" {
  role       = "${module.data_role.role_name}"
  policy_arn = "arn:aws:iam::aws:policy/AmazonRedshiftFullAccess"
}

The problem I encounter here is that when I try to run this module the above two policies are not attached in staging but in root account. How can I fix this to make it attach the policies in staging?

1

1 Answers

0
votes

I'll assume from your question that staging is its own AWS account, separate from your root account. From the Terraform docs

You can define multiple configurations for the same provider in order to support multiple regions, multiple hosts, etc.

This also applies to creating resources in multiple AWS accounts. To create Terraform resources in two AWS accounts, follow these steps.

In your entrypoint main.tf, define aws providers for the accounts you'll be targeting:

# your normal provider targeting your root account
provider "aws" {
  version = "1.40"
  region = "us-east-1"
}

provider "aws" {
  version = "1.40"
  region = "us-east-1"

  alias = "staging" # define custom alias

  # either use an assumed role or allowed_account_ids to target another account
  assume_role {
    role_arn = "arn:aws:iam:STAGINGACCOUNTNUMBER:role/Staging"
  }
}

(Note: the role arn must exist already and your current AWS credentials must have permission to assume it)

To use them in your module, call your module like this

module "data_role" {
  source = "../tf_data_role"

  providers = {
    aws.staging = "aws.staging"
    aws = "aws"
  }

  account_id         = "${var.account_id}"
  name               = "data"

  ... remainder of module
}

and define the providers within your module like this

provider "aws" {
  alias = "staging"
}

provider "aws" {}

Now when you are declaring resources within your module, you can dictate which AWS provider (and hence which account) to create the resources in, e.g

resource "aws_iam_role" "this" {
  provider = "aws.staging"  # this aws_iam_role will be created in your staging account

  name               = "${var.name}"
  description        = "${format("%s (managed by Terraform)", var.policy_description)}"
  assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}

resource "aws_iam_policy" "this" {
  # no explicit provider is set here so it will use the "default" (un-aliased) aws provider and create this aws_iam_policy in your root account
  name        = "${var.name}"
  description = "${format("%s (managed by Terraform)", var.policy_description)}"
  policy      = "${var.policy}"
}