Where to save Azure app registration secrets - azure

My questions is regarding Azure app registration secrets.
I have an app registration and its secrets (client_id, subscription_id, tenant_id and client_id)
I am able to provision the resources through terraform.
For the security purpose it is recommended to do not upload secrets to your git repo.
So my question is, where we can save these secrets so we can retrieve them at the runtime?
I have already saved them into Azure key vault and when I try to retrieve them in providers.tf I get an error
Cycle: data.azurerm_key_vault_secret.client-secret, provider["registry.terraform.io/hashicorp/azurerm"], data.azurerm_key_vault.my_key_vault
what am I doing wrong?

So finally I have found the solution on terraform's official website
First you have to set env variables in your .zprofile
export ARM_SUBSCRIPTION_ID=*******
export ARM_CLIENT_ID=*******
export ARM_CLIENT_SECRET=*******
export ARM_TENANT_ID=*******
Then you have to create a variables.tf file and then create variable blocks as
variable "subscription_id" {
}
variable "client_id" {
}
variable "client_secret" {
}
variable "tenant_id" {
}
and the last step is to access them in the providers.tf file
# Configuration options
provider "azurerm" {
subscription_id = var.subscription_id
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
features {
}
}

Related

terraform still looking for azure cli login when using Azure Service Principal

I'm fairly new to Terraform. I'm trying to use Azure Service Principal for terraform. My code looks like below. However when I run terraform init, I get an error Error building ARM Config: obtain subscription() from Azure CLI: parsing json result from the Azure CLI: waiting for the Azure CLI: exit status 1: ERROR: Please run 'az login' to setup account.
(I have deliberately logged out of Azure CLI to verify that the terraform code is NOT using azure cli. And looks like it's still looking for Azure CLI.)
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.0.0"
}
}
backend "azurerm" {
resource_group_name = "rg-XXXX"
storage_account_name = "saXXXXX"
container_name = "XXXXX"
key = "XXXXX"
}
}
provider "azurerm" {
features {}
subscription_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
client_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
client_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
tenant_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
There are missing info allowing terraform to connect to the azurerm backend.
https://developer.hashicorp.com/terraform/language/settings/backends/azurerm#resource_group_name-2
You need to add client_id, client_secret and tenant_id in the backend configuration
backend "azurerm" {
resource_group_name = "rg-XXXX"
storage_account_name = "saXXXXX"
container_name = "XXXXX"
key = "XXXXX"
client_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
client_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
tenant_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
You may use environment variables for this. You won't have to repeat them and it will allow you to commit your template to a source code repository without sensitive information.

terraform Error: Cycle: data.azurerm_key_vault_secret

This is my first file main.tf
# Configure the Azure provider
provider "azurerm" {
environment = "public"
features {}
subscription_id = var.azure-subscription-id
client_id = var.azure-client-id
client_secret = "${data.azurerm_key_vault_secret.azure-client-secret1.value}"
tenant_id = var.azure-tenant-id
}
I have another file kv-data.tf
data "azurerm_key_vault_secret" "azure-client-secret1" {
name = "azure-client-secret"
vault_uri = "https://kv23.vault.azure.net/"
}
But I get this error. What is the correct way to access keyvault secret?
│ Error: Cycle: data.azurerm_key_vault_secret.azure-client-secret1,
provider["registry.terraform.io/hashicorp/azurerm"]
It is not possible to do this. To access Keyvault you would need credentials which is not supplied. You will have to hardcode it like other vars.

Bootstraping an Azure service account in Terraform

I am trying to write the Terraform to create an Azure "service account" and am getting quite confused by the distinction between what Azure AD calls "Applications" and "Service Principals". Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
The idea would be for a human administrator to run the Terraform, to set it up once, then those credentials could later be used to authenticate for the remaining IaC. (i.e., It's a bootstrapping exercise.)
I wish to do it in Terraform, rather than a Bash script, as it seems more explicit and fits with the rest of my IaC. This is what I have so far:
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Terraform"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
# resource "azuread_service_principal" "terraform" {
# application_id = azuread_application.terraform.application_id
# owners = [data.azuread_client_config.current.object_id]
# }
#
# resource "azuread_service_principal_password" "terraform" {
# service_principal_id = azuread_service_principal.terraform.object_id
# }
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
This runs, but later authenticating with the generated credentials gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal is properly created in the tenant.
Clearly I haven't done that -- it's commented out in the above snippet -- but that's because this is where my understanding starts to break down. Why do I need both? Why do both the application and the service principal have password resources? If I generate passwords for both, which is the ARM_CLIENT_SECRET (I think the application password is the right one)? Then there's the role assignment: I see there's an azuread_app_role_assignment resource, but I'm having trouble unpicking it.
I am trying to write the Terraform to create an Azure "service
account" and am getting quite confused by the distinction between what
Azure AD calls "Applications" and "Service Principals".
Applications can be seen from Azure AD App registrations blade Where as Service Principals are other wise know as Enterprise Applications. The difference is well documented in this Microsoft Documentation.
This runs, but later authenticating with the generated credentials
gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal
is properly created in the tenant.
This is because you have no associated service principal to that azure ad application which you have created from Terraform. The association is needed to access the application or authenticating to the azure environment with contributor role. When a App registration is created from portal it by default creates an association of AD app and Service principal , which by default results in creating a service principal for that app registration. It also applies the same concept when we use az ad sp create-for-rbac.
Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
You can use the below to mimic the above command :
provider "azurerm" {
features{}
}
provider "azuread" {}
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Ansumantest"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
resource "azuread_service_principal" "terraform" {
application_id = azuread_application.terraform.application_id
owners = [data.azuread_client_config.current.object_id]
}
resource "azurerm_role_assignment" "example" {
scope = data.azurerm_subscription.current.id
role_definition_name = "Contributor"
principal_id = azuread_service_principal.terraform.object_id
}
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
Output :
Using the above details I created a reosurce group in the subscription :
provider "azurerm" {
features{}
subscription_id = "88073b30-cadd-459e-b90b-8442c93573ae"
tenant_id = "ab078f81-xxxx-xxxx-xxxx-620b694ded30"
client_id = "c022ec46-xxxx-xxxx-xxxx-c72a9b82f429"
client_secret = "wdV7Q~8Grxxxxxxxxxxxxxx~SCwbRrKIq9"
}
resource "azurerm_resource_group" "name" {
name = "testansterraform"
location = "west us 2"
}

terraform backend state file storage using keys instead of AD account

It appears that Terraform uses Keys for backend state files when persisting to an Azure storage account. I wish to use a single storage account with dedicated folders for different service principals but without cross-folder write access. I am trying to avoid accidental overwrites of the state files by different service principals. But since Terraform is using the keys to update the storage account, every service principal technically has rights to update every file. And the developer would have to take care not to accidentally reference the wrong state file to update. Any thoughts on how to protect against this?
You can use a SAS token generated for a Container to be used by that service principal only and no other service principals .
I tested with something like below:
data "terraform_remote_state" "foo" {
backend = "azurerm"
config = {
storage_account_name = "cloudshellansuman"
container_name = "test"
key = "prod.terraform.tfstate"
sas_token = "sp=racwdl&st=2021-09-28T05:49:01Z&se=2023-04-01T13:49:01Z&sv=2020-08-04&sr=c&sig=O87nHO01sPxxxxxxxxxxxxxsyQGQGLSYzlp6F8%3D"
}
}
provider "azurerm" {
features {}
use_msi = true
subscription_id = "948d4068-xxxxx-xxxxxx-xxxxxxxxxx"
tenant_id = "72f988bf-xxxx-xxxxx-xxxxxx-xxxxxxx"
}
resource "azurerm_resource_group" "test" {
name="xterraformtest12345"
location ="east us"
}
But If I change container name to another container then I can't write as it will error out saying the authentication failed as the SAS token is for Test container not Test1 container.
For more information on how to generate SAS token for containers and how to set backend azurerm for terraform , please refer the below links:
Generate shared access signature (SAS) token for containers and blobs with Azure portal. | Microsoft Docs
Use Azure storage for Terraform remote state
OR
You can set the containers authentication method to azure ad user account, after assigning storage blob data contributor/owner role to the service principal which will use that specific container .
Then you can use something like below:
data "terraform_remote_state" "foo" {
backend = "azurerm"
config = {
storage_account_name = "cloudshellansuman"
container_name = "test1"
key = "prod.terraform.tfstate"
subscription_id = "b83c1ed3-xxxx-xxxxxx-xxxxxxx"
tenant_id = "72f988bf-xxx-xxx-xxx-xxx-xxxxxx"
client_id = "f6a2f33d-xxxx-xxxx-xxx-xxxxx"
client_secret = "y5L7Q~oiMOoGCxm7fK~xxxxxxxxxxxxxxxxx"
use_azuread_auth =true
}
}
provider "azurerm"{
subscription_id = "b83c1ed3-xxxx-xxxxxx-xxxxxxx"
tenant_id = "72f988bf-xxx-xxx-xxx-xxx-xxxxxx"
client_id = "f6a2f33d-xxxx-xxxx-xxx-xxxxx"
client_secret = "y5L7Q~oiMOoGCxm7fK~xxxxxxxxxxxxxxxxx"
features {}
}
data "azurerm_resource_group" "test" {
name="resourcegroupname"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
resource_group_name = data.azurerm_resource_group.test.name
location = data.azurerm_resource_group.test.location
address_space = ["10.254.0.0/16"]
}
Output:
If service principal doesn't have Role assigned to it for the container , then it will give error like below:
Note: For the first scenario I have used managed system identity, but the same can be achieved for service principal as well.

How to make "terraform plan" use my config file

I created an Azure service principal
I created a file called azureProviderAndCreds.tf that looks similar to the following:
variable subscription_id {}
variable tenant_id {}
variable client_id {}
variable client_secret {}
provider "azurerm" {
subscription_id = "<my-azure-subscription-id>"
tenant_id = "<tenantid-returned-from-creating-a-service-principal>"
client_id = "<appid-returned-from-creating-a-service-principal>"
client_secret = "<password-returned-from-creating-a-service-principal>"
}
I ran terraform init
I ran terraform plan
When executing terraform plan, the command asks me for the client_id.
I have this value defined in the azureProviderAndCreds.tf file. How do I get Terraform to look at my config file for that info?
I'd read through the docs to get a handle of the input variables basics.
But heres a couple quick things:
You are declaring variables and not actually using them in your provider:
provider "azurerm" {
subscription_id = "${var.subscription_id}"
tenant_id = "${var.tenant_id}"
client_id = "${var.client_id}"
client_secret = "${client_secret}"
}
You could set defaults for your variables when declaring them:
variable subscription_id {
default = "your-subscription-id"
}
Of you could create a .tfvars file like variables.tfvars and pass that when running plan:
subscription_id = "your-subscription-id"
tenant_id = "your-tenant-id"
client_id = "your-client-id"
client_secret = "your-client-secret"
terraform plan -var-file=variables.tfvars

Resources