Dynamic provider support?

Hi all,

Is there any plans to support dynamic Provider ?

Use case example :

  • I create a pool of AWS account (one to N accounts and N can increase each day)
  • In each account, I would like to create an IAM user (for Terraform credentials).

To create accounts, my AWS Provider have Master account credentials.
But to create IAM user I need a provider (with alias) for each account with correct credentials.

As I have an unknown account number … I cannot write static providers (hard coded) with each credentials.

An idea :

provider "aws" {
   for_each = toset(local.alias_list)

   alias      = each.value
   access_key = var.credentials[each.value].access_key
}

And then

resource "aws_iam_user" "builder" {
   for_each = toset(local.alias_list)

   provider = aws[each.value]

   //properties
}

But of course it doesn’t work at all :slight_smile:

Any ideas ?
Thanks for answers :wink:

Hi @opsrom,

There are a couple different ways to make this sort of design work with Terraform today.

The first way, if having all everything all in one state is important to you, is to write a shared module that takes the action required for only one account, and then to write a small program to generate a top-level Terraform configuration that calls that module once for each AWS account. This does require a special additional custom wrapper around Terraform.

The JSON variant of the Terraform language is designed to be relatively easy to generate from a programming language that has a JSON library available. In this case, the program would presumably generate a provider block and a module block for each account, ideally using assume_role to keep the individual credentials out of the configuration (credentials should be passed out-of-band except in very special circumstances):

{
  "provider": {
    "aws": [
      {
        "alias": "us-west-2_12345",
        "region": "us-west-2",
        "assume_role": "arn:aws:iam::12345:role/admin"
      },
      {
        "alias": "us-west-2_67890",
        "region": "us-west-2",
        "assume_role": "arn:aws:iam::67890:role/admin"
      }
    ]
  },
  "module": {
    "us-west-2_12345": {
      "source": "./modules/per-account",
      "providers": {
        "aws": "aws.us-west-2_12345"
      }
    },
    "us-west-2_67890": {
      "source": "./modules/per-account",
      "providers": {
        "aws": "aws.us-west-2_67890"
      }
    }
  }
}

If you generate something like the above and then run terraform init, terraform apply you will then get one instance of that ./modules/per-account module per account.


The other option, which is the one I think I would use in this situation, is to write your configuration as if it’s for only one account and then make a separate workspace for each of the accounts you want to manage, perhaps named after the account id:

terraform workspace new account-12345

Inside the configuration you can use the workspace name to select the appropriate account to assume a role from:

locals {
  # Extract the AWS account id from the workspace name
  # This will raise an error if the workspace name is
  # not following the expected naming scheme.
  aws_account_id = regex("account-(\\d+)", terraform.workspace)[0]
}

provider "aws" {
  region      = "us-west-2"
  assume_role = "arn:aws:iam::${local.aws_account_id}:role/admin"
}

Now when you want to add a new account, you can create a new workspace for it, switch to it, and run terraform apply. If you want to remove an account, you can switch to its workspace, run terraform destroy, and then delete the workspace.

The reason I prefer this option when I imagine your use-case is that it means you’ll always be working only with one account at a time, rather than applying changes to all of them at once. While applying to all of them at once can be convenient, it’s also risky and inflexible: any change you make to the configuration will affect at least one object per account which will likely lead to a plan that is hard to review, and it’ll be harder to deal with situations where one account is ready to accept a change while another is not yet.


Both of these options are in a sense handled “outside” of Terraform, rather than directly inside the configuration. Terraform might get more flexibility in its handling of providers in future, but that is not planned for the near future because it requires considerable design work to make sure that Terraform can still do all of the internal tracking of provider configurations it needs to do in a situation where they are being constructed dynamically.

Thanks you very much for your design solutions :wink:

I’m already play with a “pre run” script to generate dynamic tf file to manage this situation.
As I understand… dynamic providers are not in the roadmap :slight_smile:
I will test your another solution using workspace… really good vision too :wink:

Thank you (again)