Use Terraform Provider Block with compatibility for Role ARNs and Profiles
Date: 19 Oct 2021
The content in this ADR may be out-of-date and needing an update. For questions, please reach out to Cloud Posse
- Please read through the detailed explanation in Access Control Evolution.
Status
ACCEPTED
Context
Cloud Posse has used 2 conventions for assuming roles within the terraform provider { ... }
block.
The role_arn
Method
This was our original method of assuming roles within terraform
.
provider "aws" {
# The AWS provider to use to make changes in the DNS primary account
alias = "primary"
region = var.region
assume_role {
role_arn = coalesce(var.import_role_arn, module.iam_roles.dns_terraform_role_arn)
}
}
We used this for years together with AWS Federated IAM with SAML and had no issues. Then we started supporting AWS SSO and ran into some issues because with AWS SSO the role names are non-deterministic. As a result, we switched to the profile
method below.
The profile
Method
With the profile
method we offload the burden of determining the role_arn
to some external script that would generate the ~/.aws/config
with profiles and role mappings. This allowed us to support simultaneously the AWS Federated IAM alongside the AWS SSO method of authentication. The downside was we had to use the generator pattern to create the ~/.aws/config
, which we generally like to avoid. We painfully upgraded all of our components to use this method since we didn’t see a path forward with the role_arn
method at the time.
provider "aws" {
region = var.region
# `terraform import` will not use data from a data source, so on import we have to explicitly specify the profile
profile = coalesce(var.import_profile_name, module.iam_roles.terraform_profile_name)
}
The Hybrid Method
Now we support the hybrid method after having come full circle and once again wanting to move to use the role_arn
everywhere so we do not need to generate the AWS config. However, we also need to support customers that use the profile
method. Fortunately, @Jeremy Grodberg found a convenient way to support both methods.
provider "aws" {
region = var.region
profile = module.iam_roles.profiles_enabled ? coalesce(var.import_profile_name, module.iam_roles.terraform_profile_name) : null
dynamic "assume_role" {
for_each = module.iam_roles.profiles_enabled ? [] : ["role"]
content {
role_arn = coalesce(var.import_role_arn, module.iam_roles.terraform_role_arn)
}
}
}
Decision
DECIDED: Use the Hybrid Method to support both profile
or role_arn
for backward compatibility
Note: Until Proposed: Use AWS Federated IAM over AWS SSO is decided otherwise, our recommendation for new projects is to use role_arn
, but we continue to use the hybrid provider in public components, and therefore in client components.
Consequences
- Update all components to use the Hybrid Method.