I have created a terraform template that creates 3 file shares on a storage account using a for_each loop that is working perfectly.
I am trying to assign RBAC role assignments, scoped to each file share using a for_each loop, however I keep getting the following error and I am not sure how to achieve this?
main.tf
###########################
# RESOURCE GROUP CREATION #
###########################
resource "azurerm_resource_group" "rg" {
name = var.rg.name
location = var.rg.location
# tag is a test to see if I can get them to use a variable map
tags = "${var.tags}"
}
############################
# STORAGE ACCOUNT CREATION #
############################
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account.name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = var.storage_account.account_tier
account_replication_type = var.storage_account.account_replication_type
allow_nested_items_to_be_public = false
azure_files_authentication {
directory_type = var.storage_account.directory_type
active_directory {
storage_sid = var.storage_account.storage_sid
domain_name = var.storage_account.domain_name
domain_sid = var.storage_account.domain_sid
domain_guid = var.storage_account.domain_guid
forest_name = var.storage_account.forest_name
netbios_domain_name = var.storage_account.netbios_domain_name
}
}
}
########################################
# STORAGE ACCOUNT FILE SHARES CREATION #
########################################
resource "azurerm_storage_share" "file_shares" {
for_each = var.file_shares
name = each.value.name
storage_account_name = azurerm_storage_account.storage_account.name
quota = each.value.quota
}
########################
# RBAC ROLE ASSIGNMENT #
########################
resource "azurerm_role_assignment" "rbac" {
for_each = var.rbac
scope = azurerm_storage_share.file_shares.*.id
role_definition_name = each.value.role_definition_name
principal_id = each.value.principal_id
}
variables.tf
#######################################
# STORAGE ACCOUNT FILE SHARE SETTINGS #
#######################################
variable "file_shares" {
description = "storage account file share settings"
default = {
profiles = {
name = "profiles"
quota = "5120"
}
o365 = {
name = "o365"
quota = "5120"
}
msix = {
name = "msix"
quota = "5120"
}
}
}
#################################
# RBAC ROLE ASSIGNMENT SETTINGS #
#################################
variable "rbac" {
description = "rbac assignment to storage account, principal id is the object id of the security group listed in Azure AD"
default = {
back_office = {
role_definition_name = "Storage File Data SMB Share Contributor"
principal_id = "e93a67c7-4bfc-4bbd-a720-b26d9291fa28"
}
front_office = {
role_definition_name = "Storage File Data SMB Share Contributor"
principal_id = "0280b0c9-295a-4d75-b8d0-a092cf52dabc"
}
dev_dev = {
role_definition_name = "Storage File Data SMB Share Contributor"
principal_id = "512be349-5444-45b0-80f5-8e59046a0175"
}
dev_prod = {
role_definition_name = "Storage File Data SMB Share Contributor"
principal_id = "0a676556-cf96-4318-b229-503808da7e1c"
}
admins = {
role_definition_name = "Storage File Data SMB Share Elevated Contributor"
principal_id = "b0bde374-eb5d-4967-9a4f-cdd41fd7bb23"
}
}
}
error
╵
╷
│ Error: Unsupported attribute
│
│ on storage_account/main.tf line 51, in resource "azurerm_role_assignment" "rbac":
│ 51: scope = azurerm_storage_share.file_shares.*.id
│
│ This object does not have an attribute named "id".
╵
╷
│ Error: Unsupported attribute
│
│ on storage_account/main.tf line 51, in resource "azurerm_role_assignment" "rbac":
│ 51: scope = azurerm_storage_share.file_shares.*.id
│
│ This object does not have an attribute named "id".
╵
╷
│ Error: Unsupported attribute
│
│ on storage_account/main.tf line 51, in resource "azurerm_role_assignment" "rbac":
│ 51: scope = azurerm_storage_share.file_shares.*.id
│
│ This object does not have an attribute named "id".
╵
╷
│ Error: Unsupported attribute
│
│ on storage_account/main.tf line 51, in resource "azurerm_role_assignment" "rbac":
│ 51: scope = azurerm_storage_share.file_shares.*.id
│
│ This object does not have an attribute named "id".
╵
╷
│ Error: Unsupported attribute
│
│ on storage_account/main.tf line 51, in resource "azurerm_role_assignment" "rbac":
│ 51: scope = azurerm_storage_share.file_shares.*.id
│
│ This object does not have an attribute named "id".
╵
##[warning]Can't find loc string for key: TerraformPlanFailed
##[error]Error: TerraformPlanFailed 1
You need to use the flatten function. Add the following code to your locals :
locals {
rbac_assignment = flatten([
for rbac_key, rbac in var.rbac : [
for file_key, file in azurerm_storage_share.file_shares : {
rbac_key = rbac_key
file_key = file_key
scope = azurerm_storage_share.file_shares[file_key].resource_manager_id
role_definition_name = rbac.role_definition_name
principal_id = rbac.principal_id
}
]
])
}
}
Then in your azurerm_role_assignment resource, use the following for_each:
resource "azurerm_role_assignment" "rbac" {
for_each = { for rbac_assignment in local.rbac_assignment : "${rbac_assignment.rbac_key}.${rbac_assignment.file_key}" => rbac_assignment }
scope = each.value.scope
role_definition_name = each.value.role_definition_name
principal_id = each.value.principal_id
}
I also changed the attribute reference of the file share, instead of using the id, it must use the resource_manager_id.
Related
I've created a Terraform template that creates 2 route tables and 2 subnets using the for_each command. I am trying to associate the route tables to the two subnets, however I am struggling to do so because I don't know how to obtain the ID for the route tables and subnets as the details are not in a variable, and I'm not sure how to get that information and use it. Please may someone provide assistance?
Thank you
Main Template
# SUBNETS DEPLOYMENT
resource "azurerm_subnet" "subnets" {
depends_on = [azurerm_virtual_network.vnet]
for_each = var.subnets
resource_group_name = var.rg.name
virtual_network_name = var.vnet.config.name
name = each.value.subnet_name
address_prefixes = each.value.address_prefixes
}
# ROUTE TABLE DEPLOYMENT
resource "azurerm_route_table" "rt" {
depends_on = [azurerm_virtual_network.vnet]
for_each = var.rt
name = each.value.route_table_name
resource_group_name = var.rg.name
location = var.rg.location
disable_bgp_route_propagation = true
route = [ {
address_prefix = each.value.address_prefix
name = each.value.route_name
next_hop_in_ip_address = each.value.next_hop_ip
next_hop_type = each.value.next_hop_type
} ]
}
# ROUTE TABLE ASSOICATION
resource "azurerm_subnet_route_table_association" "rt_assoication" {
subnet_id = azurerm_subnet.subnets.id
route_table_id = azurerm_route_table.rt.id
}
Variables
# SUBNET VARIBALES
variable "subnets" {
description = "subnet names and address prefixes"
type = map(any)
default = {
subnet1 = {
subnet_name = "snet-001"
address_prefixes = ["172.17.208.0/28"]
}
subnet2 = {
subnet_name = "snet-002"
address_prefixes = ["172.17.208.32/27"]
}
}
}
# ROUTE TABLES VARIABLES
variable "rt" {
description = "variable for route tables."
type = map(any)
default = {
rt1 = {
route_table_name = "rt1"
address_prefix = "0.0.0.0/0"
route_name = "udr-azure-firewall"
next_hop_ip = "10.0.0.0"
next_hop_type = "VirtualAppliance"
}
rt2 = {
route_table_name = "rt2"
address_prefix = "0.0.0.0/0"
route_name = "udr-azure-firewall"
next_hop_ip = "10.0.0.0"
next_hop_type = "VirtualAppliance"
}
}
}
The error I get when I run terraform plan is:
│ Error: Missing resource instance key
│
│ on modules\vnet\main.tf line 74, in resource "azurerm_subnet_route_table_association" "rt_assoication":
│ 74: subnet_id = azurerm_subnet.subnets.id
│
│ Because azurerm_subnet.subnets has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ azurerm_subnet.subnets[each.key]
╵
╷
│ Error: Missing resource instance key
│
│ on modules\vnet\main.tf line 75, in resource "azurerm_subnet_route_table_association" "rt_assoication":
│ 75: route_table_id = azurerm_route_table.rt.id
│
│ Because azurerm_route_table.rt has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ azurerm_route_table.rt[each.key]
Looks like you are almost there, update the following in the subnet-route table association block, it should work:
# ROUTE TABLE ASSOICATION
resource "azurerm_subnet_route_table_association" "rt_assoication" {
subnet_id = azurerm_subnet.subnets[each.key].id
route_table_id = azurerm_route_table.rt[each.key].id
}
I want to assign my Terraform Service Principal "KV Secrets User", "KV Contributor" RBAC roles
My code is given below
Storing SQL PWD in the vault
resource "azurerm_key_vault_secret" "app-pwd" {
name = "sql-pass"
value = azurerm_mssql_server.primary.administrator_login_password
key_vault_id = data.azurerm_key_vault.my-kv.id
}
resource "azurerm_role_assignment" "kv-iam" {
for_each = data.azurerm_role_definition.builtin[each.key]
scope = var.subscription_id
role_definition_name = local.role_name[each.value]
principal_id = data.azurerm_client_config.current.id
}
provider "azurerm" {
tenant_id = var.tenant_id
client_id = var.client_id
client_secret = var.client_secret
subscription_id = var.subscription_id
features {}
}
data "azurerm_key_vault" "my-kv" {
name = "testhalvault"
resource_group_name = "Dev-Rg"
}
data "azurerm_client_config" "current" {}
data "azurerm_role_definition" "builtin" {
for_each = toset(local.role_name)
name = format("%s", each.key)
}
locals {
role_name = [
"Key Vault Secrets User",
"Key Vault Administrator"
]
}
I get the below error while doing TF Plan
│
Error: Reference to "each" in context without for_each
│
│ on resources.tf line 54, in resource "azurerm_role_assignment" "kv-iam":
│ 54: for_each = data.azurerm_role_definition.builtin[each.key]
│
│ The "each" object can be used only in "module" or "resource" blocks, and
│ only when the "for_each" argument is set.
╵
Operation failed: failed running terraform plan (exit 1)
How do i assign multiple RBAC Built-In Roles to my TF SP
Normally you would do the following:
for_each = data.azurerm_role_definition.builtin
Curently I'm trying to build dev and production environment without duplicating resource blocks. I have found that I can crate map of objects and use for loop for this.
For this I have created this piece of code that was
variable "sqlserver" {
type = map(object({
name = string
username = string
password = string
}))
}
sqlserver = {
"dev" = {
name = "devsonovasqlserver"
username = "dev_username"
password = "biaJB8wQJb4n!RwG"
}
"prd" = {
name = "testexamplesqlsonova"
username = "prd_username"
password = "biaJB8wQJb4asdan!RwG"
}
}
resource "azurerm_sql_server" "sql_server" {
for_each = var.sqlserver
name = each.value["name"]
resource_group_name = var.dev_main_rg
location = var.location
version = "12.0"
administrator_login = each.value["username"]
administrator_login_password = each.value["password"]
}
This sadly raise Error like
╷
│ Error: Incorrect attribute value type
│
│ on main.tf line 56, in resource "azurerm_sql_server" "dev_sql_server":
│ 56: name = var.sqlserver.name
│ ├────────────────
│ │ var.sqlserver.name is a object, known only after apply
│
│ Inappropriate value for attribute "name": string required.
╵
Your code is valid. When I copy it to a project of my own it works fine. I guess you have something else in your files that make it work different from what is shown here.
I am trying to create multiple users using terraform. For now I am declaring them as locals but later on i will be using json file to create multiple users in my azuread environment.
Here is how i declare the locals:
locals {
users = [
[
"user1",
"Example User1",
"Password#1234#"
],
[
"user2",
"Example User2",
"Password#09876#"
]
]
}
But when I use the below code I am getting an error:
resource "azuread_user" "test" {
for_each = local.users
user_principal_name = "${each.value[0]}#tenantname.OnMicrosoft.com"
display_name = each.value[1]
mail_nickname = each.value[0]
password = each.value[2]
}
Error:
╷
│ Error: Invalid for_each argument
│
│ on main.tf line 18, in resource "azuread_user" "test":
│ 18: for_each = local.users
│ ├────────────────
│ │ local.users is tuple with 2 elements
│
│ The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.
╵
Will really appreciate any help on how to resolve this?
You have to covert it to a map:
resource "azuread_user" "test" {
for_each = {for idx, user in local.users: idx => user}
user_principal_name = "${each.value[0]}#M365B109047.OnMicrosoft.com"
display_name = each.value[1]
mail_nickname = each.value[0]
password = each.value[2]
}
As you have been told in the other answer, you need it to be a map, but before doing a for as in the other answer, it would use terraform's own tomap () function.
resource "azuread_user" "test" {
for_each = tomap({users=local.users})
user_principal_name = "${each.value[0]}#M365B109047.OnMicrosoft.com"
display_name = each.value[1]
mail_nickname = each.value[0]
password = each.value[2]
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}"
}