azure – Subnet Not Creating with Terraform on Azure, How to Fix It?

azuresubnetterraform

I am trying to create two centos 8 machines with terraform on azure.

My templates github link

When I try to apply, I am getting below error related to policy.
Could you please suggest how to fix this?

>     │ Error: creating Subnet: (Name "subnetforAutomation" / Virtual Network Name "vnetforAutomation" / Resource Group "automation_mart"):
> network.SubnetsClient#CreateOrUpdate: Failure sending request:
> StatusCode=0 -- Original Error: Code="RequestDisallowedByPolicy"
> Message="Resource 'subnetforAutomation' was disallowed by policy.
> Policy identifiers:
> '[{\"policyAssignment\":{\"name\":\"Deny-Subnet-Without-Nsg\",\"id\":\"/providers/Microsoft.Management/managementGroups/QSFT-landingzones/providers/Microsoft.Authorization/policyAssignments/Deny-Subnet-Without-Nsg\"},\"policyDefinition\":{\"name\":\"Subnets
> should have a Network Security Group
> \",\"id\":\"/providers/Microsoft.Management/managementGroups/QSFT/providers/Microsoft.Authorization/policyDefinitions/Deny-Subnet-Without-Nsg\"}}]'."
> Target="subnetforAutomation"
> AdditionalInfo=[{"info":{"evaluationDetails":{"evaluatedExpressions":[{"expression":"type","expressionKind":"Field","expressionValue":"Microsoft.Network/virtualNetworks/subnets","operator":"Equals","path":"type","result":"True","targetValue":"Microsoft.Network/virtualNetworks/subnets"},{"expression":"Microsoft.Network/virtualNetworks/subnets/networkSecurityGroup.id","expressionKind":"Field","operator":"Exists","path":"properties.networkSecurityGroup.id","result":"True","targetValue":"false"}]},"policyAssignmentDisplayName":"Deny-Subnet-Without-Nsg","policyAssignmentId":"/providers/Microsoft.Management/managementGroups/QSFT-landingzones/providers/Microsoft.Authorization/policyAssignments/Deny-Subnet-Without-Nsg","policyAssignmentName":"Deny-Subnet-Without-Nsg","policyAssignmentScope":"/providers/Microsoft.Management/managementGroups/QSFT-landingzones","policyDefinitionDisplayName":"Subnets
> should have a Network Security Group
> ","policyDefinitionEffect":"Deny","policyDefinitionId":"/providers/Microsoft.Management/managementGroups/QSFT/providers/Microsoft.Authorization/policyDefinitions/Deny-Subnet-Without-Nsg","policyDefinitionName":"Deny-Subnet-Without-Nsg"},"type":"PolicyViolation"}]
> 
>     │
>     │   with azurerm_subnet.subnet,
>     │   on main.tf line 24, in resource "azurerm_subnet" "subnet":
>     │   24: resource "azurerm_subnet" "subnet" {
>     │

I tried keeping inline, the subnet inside vnet. And the issue comes now when referring the subnet from VM instance resource block at the plan stage itself.

 Error: Unsupported attribute
│
│   on network.tf line 26, in resource "azurerm_network_interface" "example":
│   26:     subnet_id                     =  azurerm_virtual_network.vnet.subnet.id #azurerm_subnet.subnet.id
│
│ Can't access attributes on a set of objects. Did you mean to access an attribute across all elements of the set?
╵

//main.tf

## <https://www.terraform.io/docs/providers/azurerm/r/windows_virtual_machine.html>
resource "azurerm_windows_virtual_machine" "example" {
  name                = var.machine_details.name
  computer_name       = var.machine_details.name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = var.machine_details.size
  admin_username      = var.machine_details.username
  admin_password      = var.machine_details.password
  network_interface_ids = [
    azurerm_network_interface.example.id,
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }
}

//network.tf

## <https://www.terraform.io/docs/providers/azurerm/r/virtual_network.html>
resource "azurerm_virtual_network" "vnet" {
  name                = "vNet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

subnet{
 name = "internal"
 address_prefix     = "10.0.2.0/24"
 security_group = azurerm_network_security_group.example.id
}

}


## <https://www.terraform.io/docs/providers/azurerm/r/network_interface.html>
resource "azurerm_network_interface" "example" {
  name                = "example-nic"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     =  azurerm_virtual_network.vnet.subnet.id #azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.myvm1publicip.id
  }
}

resource "azurerm_public_ip" "myvm1publicip" {
  name                = var.public_ip.name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = var.public_ip.allocation_method
  sku                 = var.public_ip.sku
}

resource "azurerm_network_security_group" "example" {
  name                = var.nsg
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  security_rule {
    name                       = "test123"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

//provider.tf

## <https://www.terraform.io/docs/providers/azurerm/index.html>
provider "azurerm" {
  features {}
}

//rg.tf

## <https://www.terraform.io/docs/providers/azurerm/r/resource_group.html>
resource "azurerm_resource_group" "rg" {
  name     = "TerraformTesting2"
  location = var.location
}

//variables.tf

variable "location" {
  type        = string
  description = "Azure Region where all these resources will be provisioned"
  default     = "eastus2"
}

variable "public_ip" {
 

  default = {
      name              = "pip1"
      allocation_method = "Dynamic"
      sku               = "Basic"
    }
  
  
}

variable "nsg" {
  type        = string
  description = "Azure NSG"
  default     = "example-nsg"
}

variable "machine_details" {
  
  default = {
      name             = "example-vm2"
      size = "Standard_E2s_v3" #"Standard_F2"
      username               = "adminnasme"
      password = "MyPaword!@3"
    }
  
}

Best Answer

The subnet is failing to be created because it is not compliant with a policy your administrators have applied. This indicates that the subnet must have an NSG applied to it before it can be created. Unfortunately the way Terraform does creation of the resources is that you create the subnet first, then associate the NSG with it. This is two API calls, and the first one is failing because it doesn't have an NSG associated with it. Policy is not aware that a second call is coming to associate the NSG with the subnet.

This is the downside to the way Terraform builds on top of the ARM API. There isn't a great solution to this, other than getting your admins to relax this policy.

Edit:

So looking at this issue which is pretty similar to what you are seeing, it seems you can work around this by defining your subnets inside your virtual_network resource, rather than as separate subnet resources. Using this you can define the NSG association inline and it this may do it in a single cale:

resource "azurerm_virtual_network" "example" {
  name                = "virtualNetwork1"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  address_space       = ["10.0.0.0/16"]
  dns_servers         = ["10.0.0.4", "10.0.0.5"]

  ddos_protection_plan {
    id     = azurerm_network_ddos_protection_plan.example.id
    enable = true
  }


  subnet {
    name           = "subnet3"
    address_prefix = "10.0.3.0/24"
    security_group = azurerm_network_security_group.example.id
  }

  tags = {
    environment = "Production"
  }
}
Related Topic