Reputation: 525
I am trying to deploy a Windows virtual machine by using a contributor role using Terraform 1.10.5. I have the client_id and subscription_id and such, but I am unsure how to apply the role so I can deploy the VM with the role permissions.
I figured out that I can use data, to reference the existing resource group and network, but applying the role is a bit of a challenge. Essentially, I need to connect with a client id and assume the contributor role to deploy the VMs.
provider.tf
terraform {
required_version = ">=1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
random = {
source = "hashicorp/random"
version = "~>3.0"
}
}
}
provider "azurerm" {
features {}
resource_provider_registrations = "none"
}
variables.tf
variable "subscription_id" {
default = ""
description = "the subscription id."
}
variable "tenet_id" {
default = ""
description = "The tenet id."
}
variable "tags" {
default = "test"
description = "Any tags which should be assigned to the resources in this example"
}
variable "vm_count" {
default = "1"
description = "number of virtual machines to deploy."
}
variable "resource_group_name" {
default = "rg"
description = "Name of the resource group."
}
variable "resource_vnet" {
default = "vnet"
description = "Name of the virtual network."
}
variable "resource_subnet" {
default = "snet"
description = "Name of the subnet."
}
variable "prefix_id" {
type = string
default = "vm"
description = "Prefix of the deployments name"
}
variable "prefix_id_nsg" {
type = string
default = "vm-nsg"
description = "Prefix of the deployments name"
}
variable "prefix_id_vms" {
type = string
default = "vm-00"
description = "Prefix of the deployments name"
}
main.tf
# Generate random text for a unique storage account name
resource "random_password" "password" {
length = 5
min_lower = 1
min_upper = 1
min_numeric = 1
min_special = 1
special = true
}
data "azurerm_role_definition" "contributor" {
name = "Contributor"
}
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
}
data "azurerm_virtual_network" "my_terraform_network" {
name = var.resource_vnet
resource_group_name = data.azurerm_resource_group.rg.name
}
data "azurerm_subnet" "my_terraform_subnet" {
name = var.resource_subnet
resource_group_name = data.azurerm_resource_group.rg.name
virtual_network_name = data.azurerm_virtual_network.my_terraform_network.name
}
# Create Network Security Group and rules
resource "azurerm_network_security_group" "my_terraform_nsg" {
name = var.prefix_id_nsg
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
security_rule {
name = "RDP"
priority = 1000
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "web"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# Create network interface
resource "azurerm_network_interface" "my_terraform_nic" {
count = var.vm_count
name = "${var.prefix_id}-${count.index + 1}-nic"
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfig"
subnet_id = var.resource_subnet
private_ip_address_allocation = "Dynamic"
}
}
# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "my_terraform_nic" {
count = var.vm_count
network_interface_id = azurerm_network_interface.my_terraform_nic[count.index]
network_security_group_id = azurerm_network_security_group.my_terraform_nsg.id
}
# Create virtual machine
resource "azurerm_windows_virtual_machine" "main" {
count = var.vm_count
name = "${var.prefix_id_vms}${count.index + 1}"
admin_username = "test"
admin_password = random_password.password.result
location = data.azurerm_resource_group.rg.location
resource_group_name = data.azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.my_terraform_nic[count.index]]
size = "Standard_DS1_v2"
os_disk {
name = "${var.prefix_id_vms}${count.index + 1}"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2022-datacenter-azure-edition"
version = "latest"
}
tags = "${var.tags}"
}
output "admin_pass" {
sensitive = true
value = azurerm_windows_virtual_machine.main[*].admin_password
}
# output "private_ip_addresses" {
# value = azurerm_public_ip.public_ip[*].network_interface_private_ip
# }
output "main_hostnames" {
value = azurerm_windows_virtual_machine.main[*].id
}
Upvotes: 0
Views: 67
Reputation: 2401
Contributor role in Azure confirmation before going for deployment while using Terraform
The issue seems to be the way you use data "azurerm_role_definition" because this plugin is only used to check if a role exists and lists permissions for a role but not the role availability on the service principal or user.
Unfortunately, there is no plugin available in the Azurerm provider terraform registry to fetch the corresponding role assigned to SP directly instead you can use CLI commands passed using null resource to check and confirm the existence of role assignment to SP.
In the portal, if the PIM status was active it looks something like this mentioned below
So to confirm this existence you can use the configuration mentioned below
Demo configuration:
provider "azurerm" {
features {}
subscription_id = var.subscription_id
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
}
resource "null_resource" "validate_pim_role" {
provisioner "local-exec" {
when = create
command = <<EOT
$role = az role eligibility list --assignee ${var.client_id} --scope "/subscriptions/${var.subscription_id}" | ConvertFrom-Json
if (-not $role) {
Write-Host "ERROR: Contributor role is not activated via PIM. Please activate before running Terraform."
exit 1
}
EOT
interpreter = ["pwsh", "-Command"]
}
}
resource "azurerm_resource_group" "rg" {
name = var.resource_group_name
location = var.location
depends_on = [null_resource.validate_role]
}
resource "azurerm_virtual_network" "vnet" {
name = "vnet-demo"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
address_space = ["10.0.0.0/16"]
depends_on = [azurerm_resource_group.rg]
}
resource "azurerm_subnet" "subnet" {
name = "subnet-demo"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
depends_on = [azurerm_virtual_network.vnet]
}
resource "azurerm_network_security_group" "nsg" {
name = "nsg-demo"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
security_rule {
name = "AllowRDP"
priority = 1000
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefix = "*"
destination_address_prefix = "*"
}
depends_on = [azurerm_resource_group.rg]
}
resource "azurerm_network_interface" "nic" {
name = "nic-demo"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
depends_on = [azurerm_subnet.subnet]
}
resource "azurerm_network_interface_security_group_association" "nsg_association" {
network_interface_id = azurerm_network_interface.nic.id
network_security_group_id = azurerm_network_security_group.nsg.id
depends_on = [azurerm_network_security_group.nsg]
}
resource "azurerm_windows_virtual_machine" "vm" {
name = var.vm_name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
size = "Standard_DS1_v2"
admin_username = "adminuser"
admin_password = var.admin_password
network_interface_ids = [azurerm_network_interface.nic.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2022-datacenter"
version = "latest"
}
depends_on = [azurerm_network_interface.nic]
}
Deployement:
once it finds the relevant role permission it starts provisioning the resources.
Deployment only continues if the condition to have contributor role was passed
If the Role is not active it will through an error link this
Refer:
https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-eligible-activate
Upvotes: 1