When following tutorials out there on Terraform, a consistent approach seems to be passing in authentication secrets through variables:
provider "azurerm" { features {} subscription_id = var.subscription_id client_id = var.client_id client_secret = var.client_secret tenant_id = var.tenant_id }
This is a very low friction way to handle authentication when first beginning with Terraform because the overall environment is not front of mind. This means we must also write additional lines of code to declare the variables:
variable "subscription_id" { type = string } variable "client_id" { type = string } variable "client_secret" { type = string sensitive = true } variable "tenant_id" { type = string }
Maintaining secrets is a challenge that we want to avoid. The less code that we have, the better.
Preferred Solution
Federating identity is the preferred solution. This is enabled by using OpenID Connect (OIDC) tokens. A working platform for this is GitHub Actions.
Benefits:
- No credentials to rotate
- No Terraform code required
- No need to store credentials
- Supported in major platforms (AWS, Azure, and GCP)
Backup Solution
As a fallback for local execution and otherwise, using environment variables is an option for most providers. For instance, considering our previous Azure examples, the following could establish environment variables in Bash:
export ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000" export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000" export ARM_CLIENT_SECRET="00000000-0000-0000-0000-000000000000" export ARM_TENANT_ID="00000000-0000-0000-0000-000000000000"
This automatically is picked up by Terraform and eliminates the need to handle this via code. In addition, it accomodates other changes that would beed to be handled in code that may be unanticipated, such as supporting a special use case, like a sovereign or government tenant:
export ARM_ENVIRONMENT="german"
Without relying on such a mechanism, a variable would need to be passed with a default that is already handled otherwise:
provider "azurerm" { features {} subscription_id = var.subscription_id client_id = var.client_id client_secret = var.client_secret tenant_id = var.tenant_id environment = var.environment } ... variable "environment" { type = string default = "public" }
Supports Testing
As we continue to see maturity in the testing space with Terraform, it will be more commonplace that we write tests and achieve higher levels of code coverage. Applying Test Driven Development (TDD) to our code will suggest that we should write code that is testable by nature. Removing the need for code gives us fewer lines of code to test while improving code coverage without writing more tests.
Final Thoughts
Review the documentation for the providers used to understand the nuances around authentication: