Migrating Azure Key Vault secrets from one Azure subscription to another - azure

I have multiple Azure subscriptions, and I'm working on getting the key vault from one to another. I've written a terraform program to do this. Here I'm calling the data blocks and using for each loop condition from one subscription and using the output value of those as input values in another subscription. But I'm getting the error please help. Below is the code
data "azurerm_key_vault" "ewo1" {
provider = azurerm.demo-eworx-terraform-automation
name = "demo-eworx-keyvault"
resource_group_name = "demo-eworx-rg"
}
output "vault_uri_ewo1" {
value = data.azurerm_key_vault.ewo1.vault_uri
}
data "azurerm_key_vault_secret" "ewo1" {
provider = azurerm.demo-eworx-terraform-automation
for_each = toset(["demo-eworx-terraform-automation-client-secret", "demo-eworx-terraform-automation-client-id", "demo-eworx-terraform-automation-tenant-id", "demo-eworx-terraform-automation-subscription-id"])
name = each.key
key_vault_id = data.azurerm_key_vault.ewo1.id
}
output "secret_value" {
value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
}
data "azurerm_key_vault" "ewo11" {
provider = azurerm.terraform-automation
name = "demo-bteb-keyvault"
resource_group_name = "bteb-demo-work"
}
output "vault_uri_ewo11" {
value = data.azurerm_key_vault.ewo11.vault_uri
}
resource "azurerm_key_vault_secret" "ewo11" {
provider = azurerm.terraform-automation
for_each = toset(["demo-eworx-terraform-automation-client-secret", "demo-eworx-terraform-automation-client-id", "demo-eworx-terraform-automation-tenant-id", "demo-eworx-terraform-automation-subscription-id"])
name = each.key
value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
key_vault_id = data.azurerm_key_vault.ewo11.id
}
I need to migrate azure key vaults secrets from one subscription to another subscription. I have written the above terraform code, I'm passing the data "azurerm_key_vault" "ewo1" output value as resource "azurerm_key_vault_secret" "ewo11" value input. But getting below error.
Can some check and help me in solving the issue
error message:
Error: Incorrect attribute value type
│
│ on demo-keyvault-migration.tf line 38, in resource "azurerm_key_vault_secret" "ewo11":
│ 38: value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
│ ├────────────────
│ │ data.azurerm_key_vault_secret.ewo1 is object with 4 attributes
│
│ Inappropriate value for attribute "value": string required.
╵
╷
│ Error: Incorrect attribute value type
│
│ on demo-keyvault-migration.tf line 38, in resource "azurerm_key_vault_secret" "ewo11":
│ 38: value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
│ ├────────────────
│ │ data.azurerm_key_vault_secret.ewo1 is object with 4 attributes
│
│ Inappropriate value for attribute "value": string required.
╵
╷
│ Error: Incorrect attribute value type
│
│ on demo-keyvault-migration.tf line 38, in resource "azurerm_key_vault_secret" "ewo11":
│ 38: value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
│ ├────────────────
│ │ data.azurerm_key_vault_secret.ewo1 is object with 4 attributes
│
│ Inappropriate value for attribute "value": string required.
╵
╷
│ Error: Incorrect attribute value type
│
│ on demo-keyvault-migration.tf line 38, in resource "azurerm_key_vault_secret" "ewo11":
│ 38: value = [ for secret in data.azurerm_key_vault_secret.ewo1 : secret.name]
│ ├────────────────
│ │ data.azurerm_key_vault_secret.ewo1 is object with 4 attributes
│
╵

There are a couple of issues here, but the primary one is related to the azurerm_key_vault_secret data source. You are querying the data source while using for_each. That means that the result will be an object with key value pairs. That is why you are getting this in the output:
data.azurerm_key_vault_secret.ewo1 is object with 4 attributes
as in for_each you will use four keys:
for_each = toset(["demo-eworx-terraform-automation-client-secret", "demo-eworx-terraform-automation-client-id", "demo-eworx-terraform-automation-tenant-id", "demo-eworx-terraform-automation-subscription-id"])
The easiest and probably the cleanest way to fix the error is as follows:
resource "azurerm_key_vault_secret" "ewo11" {
provider = azurerm.terraform-automation
for_each = data.azurerm_key_vault_secret.ewo1
name = each.key
value = each.value.value
key_vault_id = data.azurerm_key_vault.ewo11.id
}
Here it is a bit unfortunate that each.value.value [1] has to be used due to the attribute naming, but there is not another way. Also, please make sure you understand how the for_each meta-argument [2] works.
[1] https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secret#value
[2] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each

Related

Parse yaml file to run a for_each loop in terraform for multiple resources creation

I have the following config.yaml file
entities:
admins:
someone#somewhere.com
viewers:
anotherone#somewhere.com
And I want to create vault entities based on the users below the yaml nodes admins and viewers
locals {
config = yamldecode(file("config.yaml"))
admins = keys(local.config["entities"]["admins"]...)
viewers = keys(local.config["entities"]["viewers"]...)
}
resource "vault_identity_entity" "admins" {
for_each = toset(local.admins)
name = each.key
policies = ["test"]
metadata = {
foo = "bar"
}
}
resource "vault_identity_entity" "viewers" {
for_each = toset(local.viewers)
name = each.key
policies = ["test"]
metadata = {
foo = "bar"
}
}
The code above fails with:
│ Error: Invalid expanding argument value
│
│ on ../../../../entities/main.tf line 3, in locals:
│ 3: admins = keys(local.config["entities"]["admins"]...)
│ ├────────────────
│ │ while calling keys(inputMap)
│ │ local.config["entities"]["admins"] is "someone#somewhere.com"
│
│ The expanding argument (indicated by ...) must be of a tuple, list, or set
│ type.
╵
╷
│ Error: Invalid index
│
│ on ../../../../entities/main.tf line 4, in locals:
│ 4: viewers = keys(local.config["entities"]["viewers"]...)
│ ├────────────────
│ │ local.config["entities"] is object with 2 attributes
│
│ The given key does not identify an element in this collection value.
How should I structure my yaml file?
It seems like you want the YAML to be a hash of a hash of a list of strings. You can restructure for that like:
entities:
admins:
- someone#somewhere.com
viewers:
- anotherone#somewhere.com
This will recast to map(map(list(string))) when yamldecode from a YAML format string to HCL2.
However, you are also attempting to convert the type with the ellipsis operator ... in your locals, and returning only the keys. I am unsure why you are doing that, and both should be removed:
admins = local.config["entities"]["admins"]
viewers = local.config["entities"]["viewers"]
Afterwards, you can convert to a set type with toset like you are doing already, and then leverage that within the for_each meta-argument as per usual:
for_each = toset(local.admins)
for_each = toset(local.viewers)
This will result in the desired behavior.

module. is a object, known only after apply

I'm trying to create a ssm parameter in aws for dyanamo db table with name and arn so it can be referenced by another application. I've tried a few different things but can't figure out what I'm doing wrong here. Can anyone tell me what is wrong here?
Thank you
Here's my main file with the module.
main.tf
locals {
prefix = "/this/is/a/test"
}
module "test_table" {
source = "git#github.com:test/terraform-modules.git//dynamodb"
name = "dynamo-${local.environment}"
ssm_parameter_prefix = local.prefix
tags = {
Environment = local.environment
}
}
resource "aws_ssm_parameter" "table_name" {
provider = aws.east
name = "${local.prefix}/new/table-name"
type = "String"
value = module.test_table.name
}
resource "aws_ssm_parameter" "table_arn" {
provider = aws.east
name = "${local.prefix}/new/table-arn"
type = "String"
value = module.test_table.arn
}
Here is the output
outputs.tf
output "test_table" {
value = module.test_table
}
output "table_arn" {
value = module.test_table.arn
}
output "table_name" {
value = module.test_table.name
}
Terraform Error
│ Error: Unsupported attribute
│
│ on dynamo.tf line, in resource "aws_ssm_parameter" "table_name":
│ 118: value = module.test_table.name
│ ├────────────────
│ │ module.test_table is a object, known only after apply
│
│ This object does not have an attribute named "name".
╵
╷
│ Error: Unsupported attribute
│
│ on dynamo.tf line, in resource "aws_ssm_parameter" "table_arn":
│ 125: value = module.test_table.arn
│ ├────────────────
│ │ module.test_table is a object, known only after apply
│
│ This object does not have an attribute named "arn".
╵
The output blocks you showed declare output values named table_arn and table_name, but your references are to module.test_table.name and module_test.table.arn.
You'll need to either change the output value names to match the references, or change the references to match the output value names. Specifically, you'll need to either remove the table_ prefix from each of your output value names, or add table_ to the front of the references like module.test_table.table_name and module.test_table.table_arn.

terraform Invalid value for "seqs" parameter: all arguments must be lists or tuples; got string

Why i'm not using the ID directly:
I have multiple datalake's where the filesystem is deployed. It throws error "resource not found" during the deployment.
What i'm trying to achieve now:
i am trying to use concat function and create the ID's. which is throwing an error.
module.adlsfs["adlsfilesystem1"].time_sleep.wait_few_mins_fs: Refreshing state... [id=2022-07-23T21:45:55Z]
╷
│ Error: Invalid function argument
│
│ on ../../../tf-core-module/adls/fs/filesystem.tf line 20, in resource "azurerm_storage_data_lake_gen2_filesystem" "storagedlsgen2fs":
│ 20: storage_account_id = concat("/subscriptions/",data.azurerm_subscription.current.id,"/resourceGroups/rsg-test/providers/Microsoft.Storage/storageAccounts/",each.value.staname)
│
│ Invalid value for "seqs" parameter: all arguments must be lists or tuples; got string.
╵
╷
│ Error: Invalid function argument
│
│ on ../../../tf-core-module/adls/fs/filesystem.tf line 20, in resource "azurerm_storage_data_lake_gen2_filesystem" "storagedlsgen2fs":
│ 20: storage_account_id = concat("/subscriptions/",data.azurerm_subscription.current.id,"/resourceGroups/rsg-test/providers/Microsoft.Storage/storageAccounts/",each.value.staname)
│
│ Invalid value for "seqs" parameter: all arguments must be lists or tuples; got string.
data "azurerm_subscription" "current" {
}
locals {
staname = toset([
for pair in sort(var.sta_name) : {
staname = pair
}
])
}
//**********************************************************
// Create File System in Datalake
//**********************************************************
resource "azurerm_storage_data_lake_gen2_filesystem" "storagedlsgen2fs" {
for_each = { for p in local.staname : jsonencode(p) => p }
name = var.adlsfilesystems
storage_account_id = concat("/subscriptions/",data.azurerm_subscription.current.id,"/resourceGroups/resourcegroup/providers/Microsoft.Storage/storageAccounts/",each.value.staname)
}
Is it even possible to use the function here? and how can i solve this.
thank you
I think that instead of concat, you want join:
storage_account_id = join("",["/subscriptions/",data.azurerm_subscription.current.id,"/resourceGroups/resourcegroup/providers/Microsoft.Storage/storageAccounts/",each.value.staname])

Computed attributes cannot be set : org_id

I am learning how to use Terraform. My aim is to deploy an architecture on GCP so here's my main.tf so far :
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "3.85.0"
}
}
}
provider "google" {
credentials = file(var.credentials_file)
region = var.region
zone = var.zone
}
data "google_organization" "org" {
domain = var.organization.display_name
org_id = var.organization.id
directory_customer_id = var.organization.directory_customer_id
}
resource "google_folder" "shared" {
display_name = "Shared"
parent = google_organization.org_id
}
resource "google_folder" "ddm" {
display_name = "Data and Digital Marketing"
parent = google_folder.shared.name
}
resource "google_folder" "dtl" {
display_name = "DTL"
parent = google_folder.ddm.name
}
According to the documentation, org_id is within the Attributes Reference
But I get the following errors:
╷
│ Error: Computed attributes cannot be set
│
│ with data.google_organization.org,
│ on main.tf line 17, in data "google_organization" "org":
│ 17: org_id = var.organization.id
│
│ Computed attributes cannot be set, but a value was set for "org_id".
╵
╷
│ Error: Computed attributes cannot be set
│
│ with data.google_organization.org,
│ on main.tf line 18, in data "google_organization" "org":
│ 18: directory_customer_id = var.organization.directory_customer_id
│
│ Computed attributes cannot be set, but a value was set for "directory_customer_id".
╵
╷
│ Error: Reference to undeclared resource
│
│ on main.tf line 22, in resource "google_folder" "shared":
│ 22: parent = google_organization.org_id
│
│ A managed resource "google_organization" "org_id" has not been declared in the root module.
What am I doing wrong?
The organization is set as a data source, but in the previous code, it is used like a resource block.
What needs to be done to reference the organization is this :
data "google_organization" "org" {
organization = var.organization.id
}
org_id is an output, not an input. The only acceptable inputs are organization ordomain; they are mutually exclusive.
And use its outputs like this :
resource "google_folder" "shared" {
display_name = "Shared"
parent = data.google_organization.org.org_id
}
EDIT : This, although syntactically correct, it might not work because the account used must be organization administrator on the organization level. I do not recomment using the google_organization data sourcejust to fetch the ID and other info, I ended up writing those in a variable and just calling it this way :
resource "google_folder" "shared" {
display_name = "Shared"
parent = "organizations/${var.organization.id}"
}

Output CSV file using terraform

I'm trying to use terraform variable data (CSV file) to create a resource group and the name of the resource group are added into the CSV file.
I'm currently experiencing the below error.
provider "azurerm" {
features{}
}
locals {
resource_groupname = csvdecode(file("./test.csv"))
}
resource "azurerm_resource_group" "Main" {
count = length(locals.resource_groupname)
name = locals.resource_groupname[count.index].groupname
location = "North europe"
}
Error Message
Error: Reference to undeclared resource
│
│ on testvariable.tf line 10, in resource "azurerm_resource_group" "Customer11":
│ 10: count = length(locals.groupname)
│
│ A managed resource "locals" "groupname" has not been declared in the root module.
╵
╷
│ Error: Reference to undeclared resource
│
│ on testvariable.tf line 11, in resource "azurerm_resource_group" "Customer11":
│ 11: name = data.locals.groupname[count.index].groupname
│
│ A data resource "locals" "groupname" has not been declared in the root module.
╵
Updated Error Messgae
╷
│ Error: Reference to undeclared resource
│
│ on testtf.tf line 10, in resource "azurerm_resource_group" "Main":
│ 10: count = length(locals.resource_groupname)
│
│ A managed resource "locals" "resource_groupname" has not been declared in the root module.
╵
╷
│ Error: Reference to undeclared resource
│
│ on testtf.tf line 11, in resource "azurerm_resource_group" "Main":
│ 11: name = locals.resource_groupname[count.index].groupname
│
│ A managed resource "locals" "resource_groupname" has not been declared in the root module.
Your code should be (assuming this time you posted correct code):
resource "azurerm_resource_group" "Main" {
count = length(local.resource_groupname)
name = local.resource_groupname[count.index].groupname
location = "North europe"
}
Since ./test.csv is not shown its difficult to speculate on its content and the use in your code.
Suppose your CSV file has headers like
name, location, ABC, XXY, CDF, your
then you use for_each as well here
locals {
resource_groupname = csvdecode(file("./test.csv"))
}
resource "azurerm_resource_group" "Main" {
for_each = { for inst in locals.resource_groupname) : inst.location=> inst
}
name = each.value.name
location = each.value.location
}

Resources