Having the Terraform azure state file under different subscription - azure

I have two subscriptions in Azure. Let's call them sub-dev and sub-prod. Under sub-dev I have resources for development (in a resource group rg-dev) and under sub-prod resources for production (in a resource group rg-prod).
Now, I would like to have only one state-file for both dev and prod. I can do this as I am using Terraform workspaces (dev and prod). There is a Storage Account under sub-dev (rg-dev) named tfsate. It has a container etc. The Azure backend is configured like this:
terraform {
backend "azurerm" {
resource_group_name = "rg-dev"
storage_account_name = "tfstate"
container_name = "tfcontainer"
key = "terraform.tfstate"
}
}
If I want to apply to the dev environment I have to switch Az Cli to the sub-dev. Similarly, for production, I would have to use sub-prod. I switch the default subscription with az cli:
az account set -s sub-prod
Problem is that the state's storage account is under sub-dev and not sub-prod. I will get access errors when trying to terraform init (or apply) when the default subscription is set to sub-prod.
Error: Failed to get existing workspaces: Error retrieving keys for Storage Account "tfstate": storage.AccountsClient#ListKeys: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client 'user#example.com' with object id '<redacted>' does not have authorization to perform action 'Microsoft.Storage/storageAccounts/listKeys/action' over scope '/subscriptions/sub-prod/resourceGroups/rg-dev/providers/Microsoft.Storage/storageAccounts/tfstate' or the scope is invalid. If access was recently granted, please refresh your credentials."
I have tried couple of things:
I added subscription_id = "sub-dev"
I generated a SAS token for the tfstate storage account and added the sas_token config value (removed resource_group_name)
but in vain and getting the same error.
I tried to az logout but terraform requires me to login first. Do I have to tune the permissions in the Azure end somehow (this is hard as the Azure environment is configured by a 3rd party) or does Terraform support this kind of having your state file under different subscription setup at all?

For better or worse (I haven't experimented much with other methods of organising terraform) we use terraform in the exact way you are describing. A state file, in a remote backend, in a different subscription to my resources. Workspaces are created to handle environments for the deployment.
Our state files are specified like this:
terraform {
required_version = ">= 0.12.6"
backend "azurerm" {
subscription_id = "<subscription GUID storage account is in>"
resource_group_name = "terraform-rg"
storage_account_name = "myterraform"
container_name = "tfstate"
key = "root.terraform.tfstate"
}
}
We keep our terraform storage account in a completely different subscription to our deployments but this isn't necessary.
When configuring your state file like so, it authenticates to the remote backend via az CLI, using the context of the person interacting with the CLI. This person needs to have the "Reader & Data Access" role to the storage account in order to dynamically retrieve the storage account keys at runtime.
With the above state file configured, executing Terraform would be
az login
az account set -s "<name of subscription where you want to create resources>"
terraform init
terraform plan
terraform apply

There's another way to do that. You can use the Access Key associated with the Storage Account on the other subscription(the one you want to have the state files on) and export it as an environment variable.
Bash:
export ARM_ACCESS_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
Powershell:
$env:ARM_ACCESS_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
Then switch to the subscription you want to deploy to and deploy.

Related

Error: Failed to get existing workspaces: containers.Client#ListBlobs:

Error: Failed to get existing workspaces: containers.Client#ListBlobs: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailure" Message="This request is not authorized to perform this operation.\nRequestId:XXX"
I am using azure storage as Terraform backend. It was working fine. I removed a private endpoint for the storage from configuration and did terraform apply. It starts giving me this error. Is there need of Private endpoint for storing Terraform state in Azure storage? Also not sure why got above error. I am unable to do terraform init with this error.
I tried in my environment and got below results:
Main
provider "azurerm" {
features{
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}
provider "azuread" {
}
data "azurerm_resource_group" "example" {
name = "< Resource group name >"
}
data "azurerm_storage_account" "example" {
name = "venkat123"
resource_group_name = data.azurerm_resource_group.example.name
}
terraform {
backend "azurerm" {
resource_group_name = "< Resource group name >"
storage_account_name = "venkat123"
container_name = "test"
key = "terraform.tfstate"
}
}
Before running the code make sure you have make sure you were logged in with your credentials:
az login --tenant <tenant ID>
az account set --subscription <subscription ID>
Console:
Yes, you can access the storage account without private endpoints.
Portal:
containers.Client#ListBlobs: Failure responding to request:
StatusCode=403 -- Original Error: autorest/azure: Service returned an
error. Status=403 Code="AuthorizationFailure" Message="This request is
not authorized to perform this operation.\nRequestId:XXX"
The above error shows that doesn't has proper permission to authorize the azure blob storage.
Check the firewall settings whether, In networking
If you are access in public enable the select all network
If you enabled selected networks add the virtual networks. and add your add your client iP address and also enable "Allow trusted Microsoft services to access this storage account" allows you to access storage account.
Make sure that you have the necessary permission, such as the Contributor and User Access Administrator roles and the Storage Blob Data Owner role.
Reference:
Creating Azure Storage Containers in a storage account with network rules, with Terraform by Ansuman Bal

Terraform vm deployment using shared galery issue

I'm implementing a Terraform template, that deploys an Azure VM, based on a custom image that resides on another tenant. I've provided permissions to an AppRegistration, and validated that using Az CLI I can deploy a VMSS referring to that same shared image.
However, if I use Terraform to deploy the VM, I get this error:
Error: compute.VirtualMachinesClient#CreateOrUpdate: Failure sending request: StatusCode=403 -- Original Error: Code="LinkedAuthorizationFailed" Message="The client has permission to perform action 'Microsoft.Compute/galleries/images/versions/read' on scope '/subscriptions//resourceGroups/RG-Images/providers/Microsoft.Compute/virtualMachines/VM1', however the current tenant '' is not authorized to access linked subscription '***'."
Terraform is using the AppRegistration that was created. however, it fails with that error
I've followed this how-to, successfully, that usees Az cli.
https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/share-images-across-tenants
I understand by the error message, that the user has the permissions, but the issue is between the 2 tenants, is that it? What else can I do to fix this?
Initially please check with the RBAC permissions on the two tenants like Virtual machine contributor or Network Contributor role .
This issue with cross tenant may be even fixed in terraform azurerm
provider version 1.34.0 or later
provider "azurerm" {
version = "~> 1.34.0"
}
And you can make use of auxiliary_tenant_ids = ["<tenant2 Id>"] to mention both the tenants while using shared image gallery .See shared image gallery /terraform/github.com by #rajaie-algorithmia
provider "azurerm" {
subscription_id = "${var.subscription_id}"
client_id = "${var.client_id}"
client_secret = "${var.client_secret}"
tenant_id = "${var.tenant_id}"
auxiliary_tenant_ids = ["${var.sig_tenant_id}"] #give the other tenant Id here
}
References:
share-images-across-tenants | microsoft docs
azure portal : how-to-share-gallery-vm-images-across-azure-tenants |Ajay varma| axiom

azure cli $Path error running in terraform cloud

Setting up terraform cloud for the first time and getting this error. Not sure why as on my local machine azure CLI is installed and the path is set, but I think has something to do with setting it in the terraform cloud platform.
Error: building AzureRM Client: please ensure you have installed Azure CLI version 2.0.79 or newer. Error parsing json result from the Azure CLI: launching Azure CLI: exec: "az": executable file not found in $PATH.
with provider["registry.terraform.io/hashicorp/azurerm"]
on versions.tf line 21, in provider "azurerm":
provider "azurerm" {
My currently tf code
versions.tf
terraform {
cloud {
organization = "myorg"
workspaces {
name = "dev"
}
}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.10.0"
}
}
required_version = ">= 1.2.3"
}
provider "azurerm" {
features {}
}
variables.tf
variable "tenant_id" {
description = "tenant id for azure subscription"
}
main.tf
resource "azurerm_resource_group" "testrg" {
name = "test-rg"
location = "Central US"
}
not doing anything fancy, but not sure how to get past the azure CLI error. I know where variables can be set in the terraform cloud platform, but not specifically where to set a $Path for the azure cli or even how to install azure cli in terraform cloud. On my local machine, I am logging in with az login on an account with sufficient permissions to the subscription.
I'm trying to "boil down" kavya Saraboju's answer, which is formally correct, to a bare minimum that helped me.
The Error message seems to be very confusing, if it has anything at all to do with the actual problem. I had to set the environment variables ARM_CLIENT_ID, ARM_TENANT_ID, ARM_CLIENT_SECRET and ARM_SUBSCRIPTION_ID in Terraform Cloud. Go to Terraform Cloud's web admin panel, choose your workspace, click on "Variables" and set all the required values:
Read here how to obtain the values for those variables.
I'm a beginner as well on both Terraform and Azure, but I anyway hope this answer will help anybody who stumbles across this issue.
And also, my solution is described comprehensively in this tutorial.
It looks like , you are trying to login using az login.This works for local terraform runs .
To authenticate in terraform cloud instance , you may need to use Terraform Cloud workspace variables .
Please make sure to complete below steps:
Please check , if you have created service principal. If
you're using Azure Clouds for example US Government .In the first
step you need to configure the Azure CLI to work with that Cloud.
$ az cloud set --name AzureUSGovernment
Then log in using az login and check for the subscriptions listed
and set it up for one.
$ az login
$ az account list
$ az account set --subscription="SUBSCRIPTION_ID"
Now please try to create the Service Principal which will have
permissions to actually manage resources in that particular
specified Subscription which had been set in the previous step.
$ az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/SUBSCRIPTION_ID"
Where you can get tenant id, clientId etc which can be
used as environment variables later. This document on creating a
service principal using the azure-cli | Terraform Registry
will guide you in detail
Now in terraform cloud workers , as we cant use az login, we can
logout of it and set the environment variables something like below
from the obtained values from previous steps.
$ export ARM_CLIENT_ID="xxxxxxxxxx"
$ export ARM_SUBSCRIPTION_ID="xxxxxxx"
$ export ARM_TENANT_ID="xxxxxx"
$ export ARM_CLIENT_SECRET="xxxxxxx"
see Configuring the Service Principal in Terraform
Then you can specify Terraform and Provider blocks
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.0.0"
}
}
}
# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
}
Then try to run terraform init > terraform plan or terraform apply which can
probably make it possible to authenticate and make terraform to run
using the Service Principal .
Reference: Using the Azure Provider with Terraform Cloud - Terraform - HashiCorp Discuss
Also do check if you have latest version of terraform, if not install and try with it.

Azure DevOps Release - terraform import fails with 'Authenticate using a Service Principal'

I have a project that is running on Azure DevOps that requires creating a KeyVault and giving a series of managed AppService identities access to secrets in that vault.
Because of Terraform not being able to give its own service connection access to the key vault(this is a bug of some kind), I am forced to create ResourceGroup and Keyvault with SP access before Terraforming.
When running terraform import on resourcegroup and Keyvault through a PowerShell task:
terraform init
$state = terraform state list
if ($state -like '*azurerm_resource_group.instancerg*' -and '*azurerm_key_vault.instancekeyvault*') {
Write-Host "Resources have already been imported!"
}
else {
terraform import azurerm_resource_group.instancerg /subscriptions/$(subscriptionid)/resourceGroups/rgname
terraform import azurerm_key_vault.instancekeyvault /subscriptions/$(subscriptionid)/resourceGroups/rgname/providers/Microsoft.KeyVault/vaults/keyvaultname
}
Failure happens on terraform import commands:
'Authenticate using a Service Principal' To authenticate to Azure
using a Service Principal, you can use the separate auth method -
instructions for which can be found here:'
My main.tf contains:
provider "azurerm" {
version = "=2.7.0"
subscription_id = var.subscriptionid
client_id = var.devopsserviceconnectionaid
client_secret = var.devopsserviceconnectionpw
tenant_id = var.tennantid
features {}
}
The variables are all linked to the proper credentials.
From what I understand Terraform should pick up on what authentication method that is being used based on the credentials that are in the block above or specific env variables (that are also present...) but somehow Terraform still thinks I'm trying to auth through Azure Cli and not Service principal.
You can use manage identities in keyvault in terraform as shown below.
object_id = azurerm_app_service.app.identity.0.principal_id
Web app is as below creating managed identity
KV as below
The order should be create web app with managed identity, then the KV then the KV access policy.
For authenticate with Azure pipelines service connection below works fine but you need to pass the arguments via the pipeline.
for further information check this blog here
Full PowerShell based implementation calling terraform with Azure DevOps pipelines is explained here. This implementation prevents any azure resources as prerequisite before terraforming. Only prerequisite is creating the SPN to enable authentication and authorization.

Hashicorp Terraform - Storing Azure Storage account access key in Azure Key Vault

Just as a disclaimer, I'm quite new to Terraform, and I'm trying to figure out how to store my Azure Storage Account Access Key in my Azure Key Vault. (Referenced here).
The specific command that is referenced is:
export ARM_ACCESS_KEY=$(az keyvault secret show --name terraform-backend-key --vault-name myKeyVault --query value -o tsv)
I get that the --vault value should be replaced with the name of my Key Vault, but what am I supposed to replace the --name value with?
And as importantly, in what file/config am I supposed to put the whole export ARM_ACCESS_KEY string?
Thanks so much, everyone!
To store the Terraform state in Azure Storage Account, the necessary resource is Storage account, but for you, you want to store your storage access key in the Azure Key Vault. So you need to create a new Key Vault or use the existing one.
You can use the CLI command to store your storage access key in your key vault like this:
az keyvault secret set --name secret_name --vault-name yourKeyvault_name --value yourStorageAccessKey
Then use the command you provide to export the environment variable ARM_ACCESS_KEY:
export ARM_ACCESS_KEY=$(az keyvault secret show --name secret_name --vault-name yourKeyvault_name --query value -o tsv)
Then you just need to follow the steps in the document that you have referred in your question.
Update
If you want to set the environment variables when you in Windows, you can do it like this:
$env:VAR_NAME='vaule'
In your issue, you could export the storage access key like this:
$env:ARM_ACCESS_KEY=(az keyvault secret show --name secret_name --vault-name yourKeyvault_name --query value -o tsv)
Change the secret_name and yourKeyvault_name into your resource. The result on my side:
--name is for keyvault secret's name.
Which file/config to put?
In Terraform the names of individual files are not significant and instead Terraform works with whole directories. The idea is to first execute this(state mechanism). I usually put it in the terraform.tf : these are the contents and backend section gets the key from env variable(as mentioned here)
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.46.0"
}
}
backend "azurerm" {
resource_group_name = "tfstate"
storage_account_name = "<storage_account_name>"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "state-demo-secure" {
name = "state-demo"
location = "eastus"
}
Another option is to use tfmake.
When using tfmake as a wrapper around terraform , setting the ARM_ACCESS_KEY can be automated using it's configuration as follows:
$PROJECT/.tfmake/config
provider: azure
environment:
- ARM_ACCESS_KEY = $(az keyvault secret show --name storage-account-key1 --vault-name kv-terraform-storage --query value -o tsv)
Using this configuration, the ARM_ACCESS_KEY will become an environment variable just before actually executing any terraform command.

Resources