Terraform azurerm data source resource group no object id - terraform

I want to create a role assignment which allows a resource group to contribute to a static ip adresse.
The role assignment looks like the following:
resource "azurerm_role_assignment" "public_ip_role" {
scope = azurerm_public_ip.ingress_nginx.id
role_definition_name = "Contributor"
principal_id = data.azurerm_resource_group.rg_aks.object_id
}
The data source looks like this:
data "azurerm_resource_group" "rg_aks" {
name = "aks-my-${var.environment}"
}
The error I get is the following:
│ This object has no argument, nested block, or exported attribute named "object_id".

Looking at the documentation [1] it should be only .id:
resource "azurerm_role_assignment" "public_ip_role" {
scope = azurerm_public_ip.ingress_nginx.id
role_definition_name = "Contributor"
principal_id = data.azurerm_resource_group.rg_aks.id
}
EDIT: Based on the comments, the error is not only because a wrong attribute of a data source is being accessed, rather a completely wrong data source is being used. As per the documentation [2], it should be azurerm_client_config:
data "azurerm_client_config" "example" {
}
resource "azurerm_role_assignment" "example" {
scope = data.azurerm_subscription.primary.id
role_definition_name = "Reader"
principal_id = data.azurerm_client_config.example.object_id
}
[1] https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group#id
[2] https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment#example-usage-using-a-built-in-role

So I found a way which works in this specific case.
The principal_id which should be used is the one of a resource group created automatically by azure when creating a k8s cluster.
I found out that the principal_id of this resource group can be found when inside the state of the created cluster.
To find the id one has to find the cluster with "terraform state list" and then "terraform state show clusterName".
The principal_id is under identity so it can be referenced with
azurerm_kubernetes_cluster.k8s.identity[0].principal_id

Related

Creating Azure role assignments over a map of object_ids and roles

I'm trying to create a Terraform deployment that will enact azurerm_role_assignment by iterating over a list of principal_object_ids and their allocated roles. My code is roughly:
Define principal_ids and the role to allocate:
locals {
subscription_access_list_by_id = {
"SPID########1" : "reader" ,
"SPID########1" : "storage blob data reader",
"SPID########2" : "owner",
"SPID########2" : "storage blob data owner"
}
}
A module to allocate roles:
resource "azurerm_role_assignment" "role" {
scope = var.subscription_id
role_definition_name = var.role_definition_name
principal_id = var.object_id
}
A main.tf including the following block:
module "access-control" {
for_each = local.subscription_access_list
source = "modules/access-control"
principal_id. = each.key
subscription_id = var.subscription_id
role_definition_name = each.value
}
What I actually end up seeing is that the last entry for a given service principal is the only one acted on. Ie, from the above example, SPID#######1 would get "storage blob data reader", but not "reader" and SPID######2 would get "storage blob date owner", but not "owner".
I'm assuming there is something going on where it only creates one block for each key, so the latest value overwrites it, but I'm not sure how to get round this without making a more complicated implementation of my subscription_access_list_by_id map.
I've tried using {for k, v in subscription_access_list_by_id : k => v } as an approach to no avail.
I tried in my environment and got below results:
Variables.tf
variable "principal_ids" {
description = "The ID of the principal that is to be assigned the role at the given scope. Can be User, Group or SPN."
type = list(string)
default = ["cbd2d6bc-0d6e-4f13-bf7e-60207d82e674" ,"e396b90e-2309-490b-984f-7a8bab4eb625"]
}
Main.tf
locals {
principal_ids = toset(var.principal_ids)
}
resource "azurerm_role_assignment" "custom" {
for_each = local.principal_ids
scope = data.azurerm_subscription.primary.id
role_definition_name = "Reader"
principal_id = each.key
}
resource "azurerm_role_assignment" "contrib" {
for_each = local.principal_ids
scope = data.azurerm_subscription.primary.id
role_definition_name = "storage blob data reader"
principal_id = each.key
}
With above code I can add the role assignments like reader and storage-blob-data-reader to the service principal id.
Console:
Portal:
The role definition name should not be list. If you need to add assign another user, you can create separate variable and assign to them.
Reference:
azure - Create Role assignment dynamically in Terraform from input - Stack Overflow

How do you give "Storage Blob Data Contributor" permission to your Azure Devops project in Terraform?

It seems my question is related to this post but since there is no answer I will ask again.
I have an Azure Devops project which I use to deploy static content into a container inside a Storage Account via Pipelines. I've recently decided to deploy my infrastructure using Terraform as well as my code but I'm running into an issue. I managed to create all my infrastructure with Terraform inside my Pipeline except for the Role Assignment.
I basically need to add a new Role Assignment to my Storage Account, through Azure it goes :
Go to my Storage Account
Go to Access Control (IAM)
Add a new Role Assignments
Select Storage Blob Data Contributor
Click on Select members
Select my Azure Devops Project
Review + assign
From what I understand in the Terraform documentation I should do something like this :
resource "azurerm_resource_group" "resource_group" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.resource_group.name
location = azurerm_resource_group.resource_group.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_role_assignment" "role_assignment" {
scope = azurerm_storage_account.storage_account.id
role_definition_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" # Which is the Storage Blob Data Contributor role if I'm not mistaken.
principal_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy" # Which should be the Application ID ?
}
Except it doesn't work, when I try to run it in local without the Azure Pipeline to check if this works, the process is stuck in the "Still creating..." state for more than 10 minutes, which seems weird since when you do it manually it only takes up to a few seconds. I don't have any error I just end up canceling the command.
What am I missing / doing wrong here ?
I've found what was the issue. For the principal_id you need to put the Object_ID of your Service Principal and not your Application_ID. You end up with something like :
main.tf
...
locals {
sub = "/subscription"
permission_storage_blob_data_contributor = "providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe"
}
data "azurerm_subscription" "primary" { }
resource "azurerm_resource_group" "resource_group" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.resource_group.name
location = azurerm_resource_group.resource_group.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_role_assignment" "role_assignment" {
scope = azurerm_storage_account.storage_account.id
role_definition_id = join("/", [local.sub, data.azurerm_subscription.primary.subscription_id, local.permission_storage_blob_data_contributor])
principal_id = var.devops_project_object_id
}
...
variables.tf
...
variable "location" {
type = string
description = "Location for the deployment"
default = "West Europe"
}
variable "resource_group_name" {
type = string
description = "Resource Group Name"
}
variable "storage_account_name" {
type = string
description = "Storage Account Name"
}
# yyyyyyyy-yyyy-yyyy-yyyyyyyyyyyy format
variable "devops_project_object_id" {
type = string
description = "Object ID (principal_id) for the Devops Project linked to the Azure Subscription in the Azure Active Directory."
}
...
Role assignment can be simplified to this call:
resource "azurerm_role_assignment" "blob_contributor" {
scope = azurerm_storage_account.storage_account.id
role_definition_name = "Storage Blob Data Contributor"
principal_id = var.devops_project_object_id
}

Terraform Error 'the number of path segments is not divisible by 2' creating Azure Configuration Keys

I'm trying to create an Azure App Configuration service and keys through Terraform, but when I run my Terraform through my pipeline I get an error running terraform plan. This is my tf script for creating the service and keys:
resource "azurerm_app_configuration" "appconf" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
tags = var.tags
sku = "standard"
}
resource "azurerm_app_configuration_key" "MainAPI" {
configuration_store_id = azurerm_app_configuration.appconf.id
key = "MainAPI"
value = var.picking_api_url
type = "kv"
label = var.environment_name
}
# other keys omitted
This is the error I see:
Error: while parsing resource ID: while parsing resource ID: the number of path segments is not divisible by 2 in "subscriptions/[SubscriptionId]/resourceGroups/[rgName]/providers/Microsoft.AppConfiguration/configurationStores/[AppConfigServiceName]/AppConfigurationKey/MainAPI/Label"
I get this error regardless of whether I explicitly include a label argument for the key in my TF script. I've bumped up my version of the Terraform ARM provider to 2.90 in case it was a bug in the provider, but still get the error.
resource "azurerm_app_configuration_key" depends on azurerm_role_assignment. You need to define a resource for that as well for assigning role to app_configuration.
The following code from the Hashicorp Terraform documentation for azurerm_app_configuration_key demonstrates how to do this:
resource "azurerm_role_assignment" "appconf_dataowner" {
scope = azurerm_app_configuration.appconf.id
role_definition_name = "App Configuration Data Owner"
principal_id = data.azurerm_client_config.current.object_id
}
resource "azurerm_app_configuration_key" "test" {
configuration_store_id = azurerm_app_configuration.appconf.id
key = "appConfKey1"
label = "somelabel"
value = "a test"
depends_on = [
azurerm_role_assignment.appconf_dataowner
]
}

Adding role assignments to multiple Azure subscriptions for a managed identity using terraform

I have an Azure function app that is hosted in subscription "sub-test1" and I want to add role assignment to give the managed system identity(for app) access to the subscription "sub-test1"(current) and I have been able to do it via the following:
data "azurerm_subscription" "current" {}
data "azurerm_role_definition" "owner" {
name = "Owner"
}
resource "azurerm_role_assignment" "custom_role_assignment" {
name = "${var.random_guid}"
scope = data.azurerm_subscription.current.id
role_definition_id = "${data.azurerm_subscription.current.id}${data.azurerm_role_definition.owner.id}"
principal_id = azurerm_function_app.app.identity.0.principal_id
}
But I need to give this app access to multiple subscriptions(dynamic number) inside the tenant, say "sub-test2","sub-test3","sub-test4",etc. What is the best way I can do it using terraform only? Also, can this be done using only one "azurerm_role_assignment" resource block as shown above or do I need multiple such blocks respective to each subscription?
For this requirement, you need to have enough permission to create the role assignment for in the subscriptions. The simplest way is that you need to have the Owner role of all the subscriptions. Then you can change the code like this to achieve what you want:
data "azurerm_subscriptions" "example" {}
data "azurerm_role_definition" "example" {
name = "Owner"
}
resource "azurerm_role_assignment" "custom_role_assignment" {
count = length(data.azurerm_subscriptions.example.subscriptions.*.subscription_id)
name = "${var.random_guid}"
scope = "/subscriptions/${element(data.azurerm_subscriptions.example.subscriptions.*.subscription_id, count.index)}"
role_definition_id = "/subscriptions/${element(data.azurerm_subscriptions.example.subscriptions.*.subscription_id, count.index)}${data.azurerm_role_definition.example.id}"
principal_id = azurerm_function_app.app.identity.0.principal_id
}
Here is something different. Use the azurerm_subscriptions instead of azurerm_subscription to get all the subscriptions. But it only gets the GUID of the subscriptions. So we need to complete the resource Id of the subscriptions ourselves. Also for the role definition.

How to attach an existing rbac role to an Azure VM using Terraform?

I'm creating an Azure Virtual Machine using Terraform. But I don't know how to attach an existing rbac role to it. Is there a way to do this without creating a separate resource for role definition/attachment?
If separate resource needed how to do it knowing only the rbac role name?
From your comment, you want to assign an RBAC role to a user with terraform. You can do it in two steps:
step1: Use this data source to access information about an existing Role Definition referring to this.
data "azurerm_subscription" "primary" {} # access an existing subscription
data "azurerm_role_definition" "custom" { # access an existing custom role via role_definition_id
role_definition_id = "${azurerm_role_definition.custom.role_definition_id}"
scope = "${data.azurerm_subscription.primary.id}" # /subscriptions/00000000-0000-0000-0000-000000000000
}
data "azurerm_role_definition" "custom-byname" { # access an existing custom role via name
name = "${azurerm_role_definition.custom.name}"
scope = "${data.azurerm_subscription.primary.id}"
}
data "azurerm_builtin_role_definition" "builtin" { # access an existing builtin role
name = "Contributor"
}
step2: Assign the role to a specific Azure AD user. For example, if you want to assign this role to a user at the resource group level, that is to define the scope with the resource group ID. You should have an existing resource group. You can create it with resource "azurerm_resource_group" block or data "azurerm_resource_group", then assigns a given Principal (User or Application) to a given Role with azurerm_role_assignment.
Example Usage (using a built-in Role)
data "azurerm_subscription" "primary" {}
resource "azurerm_resource_group" "myrg" {
name = "myrg"
location = "West US"
}
resource "azurerm_role_assignment" "test" {
scope = "${azurerm_resource_group.main.id}"
role_definition_name = "Reader" # or "${data.azurerm_role_definition.custom-byname.name}"
principal_id = "xxxx"
}
The principal_id is the Object ID of the user. You can find it via navigate to the Azure Active Directory in the portal -> Users -> search by the user principal name(email address in your case). You could refer to this answer.
What you are looking for I believe is the azurerm_role_definition data source, which allows you to import an already existing role definition into terraform.
See documentation here.
Example:
data "azurerm_subscription" "primary" {}
data "azurerm_role_definition" "my_role" {
### Specify either role_definition_id or name of the existing role
# role_definition_id = "00000000-0000-0000-0000-000000000000"
# name = "MyRoleDefinition"
scope = data.azurerm_subscription.primary.id
}
To assign this role to, for example, a resource group my_rg, set the scope to the resource group id:
resource "azurerm_resource_group" "my_rg" {
name = "myRG"
location = "West US"
}
data "azurerm_client_config" "client_config" {}
resource "azurerm_role_assignment" "my_role_assignment" {
scope = azurerm_resource_group.my_rg.id
role_definition_id = data.azurerm_role_definition.my_role.id
principal_id = data.azurerm_client_config.client_config.service_principal_object_id
}

Resources