Terraform: how to implement Application Security Groups in Azure RM - azure

Problem
I discovered that I can integrate Application Security Groups (ASG) into a Network Interface when using the azurestack resource provider, but I cannot do so when using the azurerm resource provider.
My Understanding
I do not understand why I cannot. I actually do not understand the difference between Azure Stack and Azure RM. This article suggests that Azure Stack is for hybrid deployments and Azure RM (or Azure Provider) is for pure cloud deployments.
All the previous work that I and other colleagues have done has been with azurerm. I would prefer to stick with azurerm if I could. Or, if possible, I would like to "mix and match" azurerm and azurestack, using azurestack only when I have to, like in this case. But I'd really like to know why some things are only possible with one provider, since they both should have the same offering, with respect to pure Azure services.
Any Ideas?
Ultimately, though, I am just trying to solve the problem of attaching a network interface to a VM, where the NIC has associated ASGs. I would like to do this with azurerm if possible. I can do it with azurestack, as long as azurestack is compatible with other services launched through azurerm.

There is no need to use azurestack to associate NIC with ASGs
Terraform provider azurerm has resource called azurerm_network_interface_application_security_group_association
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_application_security_group_association
You just need to create ASG and associate it with NIC.
Example:
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "example" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_application_security_group" "example" {
name = "example-asg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_network_interface" "example" {
name = "example-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_network_interface_application_security_group_association" "example" {
network_interface_id = azurerm_network_interface.example.id
application_security_group_id = azurerm_application_security_group.example.id
}

Related

terraform Vnet and Subnet Modules

I have a pipeline that downloads standard Terraform modules and creates resources.
"Module-ResourceGroup" deploys resource group.
"Module-Vnet" which deploys vnet.
"Module-Subnet" which deploys subnets.
My problem is when I kick the pipeline for the first time my pipeline fails reason because Module-Subnet gives me an error message that Vnet does not exist.
However, when I run the same pipeline for a second time, my Subnets get deployed without any issues as during the first run, then Vnet gets created.
I guess I have two solutions :
depends_on where I can say that by subnet module is dependent on vnet module.
Introduce a wait of 3 mins in the subnet module before it gets executed.
Q1. why it is happening? whereas as per terraform "Most of the time, Terraform infers dependencies between resources based on the configuration given" https://learn.hashicorp.com/tutorials/terraform/dependencies
if anything is wrong with the way I have written modules?
Q2. What is a better solution depends_on OR introduce wait
Q3. Is there any other way to fix it?
Below are my modules.
Module-ResourceGroup/main.tf
resource "azurerm_resource_group" "my-resourcegroup" {
name = format("%s-%s",var.resource_group_name,var.env)
location = var.location
}
Module-Vnet/main.tf
resource "azurerm_virtual_network" "my-vnet" {
name = format("%s-%s",var.vnet_name,var.env)
resource_group_name = format("%s-%s",var.resource_group_name,var.env)
location = var.location
address_space = var.address_space
}
Module-Subnet/main.tf
resource "azurerm_subnet" "my-subnet" {
for_each = var.subnetsconfig
name = format("%s-%s",each.key,var.env)
address_prefixes = each.value["address_prefixes"]
virtual_network_name = format("%s-%s",var.vnet_name,var.env)
resource_group_name = format("%s-%s",var.resource_group_name,var.env)
}
If you use the output of a resource as the input of another resource then Terraform will understand it as an implicit dependency. For example (as you did not post all of your code):
Module-ResourceGroup/main.tf
resource "azurerm_resource_group" "my-resourcegroup" {
name = format("%s-%s",var.resource_group_name,var.env)
location = var.location
}
Module-Vnet/main.tf
resource "azurerm_virtual_network" "my-vnet" {
name = format("%s-%s",var.vnet_name,var.env)
resource_group_name = azurerm_resource_group.my-resourcegroup.name
location = var.location
address_space = var.address_space
}
Module-Subnet/main.tf
resource "azurerm_subnet" "my-subnet" {
for_each = var.subnetsconfig
name = format("%s-%s",each.key,var.env)
address_prefixes = each.value["address_prefixes"]
virtual_network_name = azurerm_virtual_network.my-vnet.name
resource_group_name = azurerm_resource_group.my-resourcegroup.name
}

Subnet not creating

I keep getting this weird error according to me, is there a fix for this.
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
#environment = var.environment
}
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = var.location
resource_group_name = var.resource_group_name
address_space = var.address_space
}
resource "azurerm_subnet" "subnet" {
name = var.subnet_name
resource_group_name = var.resource_group_name
virtual_network_name = var.vnet_name
address_prefixes = ["10.0.0.0/24"]
service_endpoints = ["Microsoft.Sql"]
delegation {
name = "delegation"
service_delegation {
name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action"]
}
}
}
I keep getting this error
azurerm_subnet.subnet: Creating...
azurerm_virtual_network.vnet: Creating...
azurerm_virtual_network.vnet: Creation complete after 5s [id=/subscriptions/e4da9536-6759-4506-b0cf-10c70facd033/resourceGroups/rg-sagar/providers/Microsoft.Network/virtualNetworks/vnet]
╷
│ Error: creating Subnet: (Name "subnet" / Virtual Network Name "vnet" / Resource Group "rg-sagar"): network.SubnetsClient#CreateOrUpdate: Failure sending request: StatusCode=404 -- Original Error: Code="ResourceNotFound" Message="The Resource 'Microsoft.Network/virtualNetworks/vnet' under resource group 'rg-sagar' was not found. For more details please
go to https://aka.ms/ARMResourceNotFoundFix"│
│ with azurerm_subnet.subnet,
│ on main.tf line 14, in resource "azurerm_subnet" "subnet":
│ 14: resource "azurerm_subnet" "subnet" {
│
Even after the vnet is created, it is unable to create a vnet, any idea how I can make this work
Any idea how to fix this?
You need to use this statement virtual_network_name = azurerm_virtual_network.vnet.name instead of virtual_network_name = var.vnet_name.
Becuause virtual_network_name = var.vnet_name in subnet resource block simultaneously creating subnet and vnet so this is not good fit in azure. Because subnet dependent on Vnet. So Vnet Should create first. So you need to use virtual_network_name = azurerm_virtual_network.vnet.name for using the existing Vnet.
Terraform Code
provider "azurerm" {
features{}
}
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
#environment = var.environment
}
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = data.azurerm_resource_group.rg.location
resource_group_name = var.resource_group_name
address_space = var.address_space
}
resource "azurerm_subnet" "subnet"{
name = var.subnet_name
resource_group_name = var.resource_group_name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.0.0/24"]
service_endpoints = ["Microsoft.Sql"]
delegation {
name = "delegation"
service_delegation {
name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action"]
}
}
}
I think this question requires a bit more explanation, since there is nothing wrong with the code. Terraform is trying to be smart about the way it creates resources, so it tries to create as much as it can in one run. This is why there is an option called -parallelism:
-parallelism=n Limit the number of parallel resource operations.
Defaults to 10.
This means that when running terraform apply, Terraform will try to run 10 resource operations including resource creation. In your case, it will try to create both the vnet and the subnet resource (parallelism applies in apply, plan and destroy). However, since you are using the same variable in both resources (var.vnet_name), Terraform is not aware that there are dependencies between the two. The way you have structured your code now would work if you were to create the vnet first and add the subnet resource after the vnet is created. Or if you are feeling adventurous you could set the parallelism to 1. Since you probably do not want that, the best way to tell Terraform in which order to create stuff is by using resource dependencies. Terraform has a concept of implicit [1] and explicit [2] dependencies. Dependencies help Terraform decide what needs to be created, based on the graph it creates [3].
There are two options in your case:
Create an implicit dependency between vnet and subnet
Create an explicit dependency between vnet and subnet
As using depends_on (or explicit dependency) is advised only in cases where there is not another way to tell Terraform that two resources are interdependent, the best way to do it is by using the implicit dependency:
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
}
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = var.location
resource_group_name = var.resource_group_name
address_space = var.address_space
}
resource "azurerm_subnet" "subnet" {
name = var.subnet_name
resource_group_name = var.resource_group_name
virtual_network_name = azurerm_virtual_network.vnet.name # <-- implicit dependency
address_prefixes = ["10.0.0.0/24"]
service_endpoints = ["Microsoft.Sql"]
delegation {
name = "delegation"
service_delegation {
name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action"]
}
}
}
The vnet resource exports some attributes after it is created [4], including the name attribute. This helps with creating the implicit dependency: by referencing a resource and one of the attributes that is available after the resource is created, you are telling Terraform that it first needs to create the vnet and only after it is available it can start with subnet creation.
[1] https://www.terraform.io/language/resources/behavior#resource-dependencies
[2] https://www.terraform.io/language/meta-arguments/depends_on
[3] https://www.terraform.io/internals/graph#resource-graph
[4] https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network#attributes-reference

Error on adding a storage share to the Azure storage account

I'm getting the following error on running terraform apply after adding an azurerm_storage_share.
Error: Error checking for existence of existing Storage Share "fileshare"
(Account "sttestforaddingfileshare" / Resource Group "resources"):
shares.Client#GetProperties: Failure responding to request: StatusCode=403
-- Original Error: autorest/azure: Service returned an error.
Status=403 Code="AuthorizationFailure"
Message="This request is not authorized to perform this operation.
\nRequestId:188ae38b-e01a-000b-35b3-a32ea2000000
\nTime:2020-10-16T11:55:16.7337008Z"
I think the reason is most likely that Terraform tries to list existing file shares in the storage account directly accessing the storage account's REST API instead of Azure Resource Manager's REST API.
It failed because there exist firewall rules in place not containing the IP of the host terraform runs on. When I add my laptop's IP to the firewall rules, it works. But it's not the desired behavior.
Do you know any workaround? Any help is appreciated.
My TF configuration is as follows:
provider "azurerm" {
version = "= 2.32.0"
features {}
}
resource "azurerm_resource_group" "rg" {
name = "resources"
location = var.location
}
resource "azurerm_virtual_network" "vnet" {
name = "vnet"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "snet" {
name = "snet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
service_endpoints = [ "Microsoft.Storage" ]
}
resource "azurerm_storage_account" "storage" {
name = "sttestforaddingfileshare"
resource_group_name = azurerm_resource_group.rg.name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = [ azurerm_subnet.snet.id ]
bypass = [ "None" ]
}
}
resource "azurerm_storage_share" "file_share" {
name = "fileshare"
storage_account_name = azurerm_storage_account.storage.name
quota = 100
}
You can use the azurerm_storage_account_network_rules resource to define the Network Rules and remove the Network Rules block defined directly on the azurerm_storage_account resource.
Also, you can create your file share via using az CLI instead of the separate resource "azurerm_storage_share"
After my validation, with the
PS D:\Terraform> .\terraform.exe -v
Terraform v0.13.4
+ provider registry.terraform.io/hashicorp/azurerm v2.32.0
It worked when terraform apply and terraform destroy.
resource "azurerm_storage_account" "storage" {
name = "nnnstore1"
resource_group_name = azurerm_resource_group.rg.name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
provisioner "local-exec" {
command =<<EOT
az storage share create `
--account-name ${azurerm_storage_account.storage.name} `
--account-key ${azurerm_storage_account.storage.primary_access_key} `
--name ${var.myshare} `
--quota 100
EOT
interpreter = [ "Powershell", "-c"]
}
}
resource "azurerm_storage_account_network_rules" "test" {
resource_group_name = azurerm_resource_group.rg.name
storage_account_name = azurerm_storage_account.storage.name
default_action = "Deny"
virtual_network_subnet_ids = [azurerm_subnet.snet.id]
bypass = ["None"]
}
I recently ran into this issue when attempting to create a storage share for a container group. It was pretty much identical code to yours but with the additional container group.
I came across the issue when deploying the stack as new and I bypassed the error by deploying everything but the storage share component and all references to it.
Then when that was completed I introduced the storage share and redeployed without issue.
Crappy work around but its deployed again.

Getting "Error waiting for Virtual Network Rule "" (server, rg) to be created or updated..." for azurerm_mariadb_virtual_network_rule

I'm building a Terraform config for my infrastructure deployment, and trying to connect an azurerm_mariadb_server resource to an azurerm_subnet, using an azurerm_mariadb_virtual_network_rule, as per documentation.
The vnet, subnet, mariadb-server etc are all created, but I get the following when trying to create the vnet_rule.
Error: Error waiting for MariaDb Virtual Network Rule "vnet-rule" (MariaDb Server: "server", Resource Group: "rg")
to be created or updated: couldn't find resource (21 retries)
on main.tf line 86, in resource "azurerm_mariadb_virtual_network_rule" "vnet_rule":
86: resource "azurerm_mariadb_virtual_network_rule" "mariadb_vnet_rule" {
I can't determine which resource can't be found - all resources except the azurerm_mariadb_virtual_network_rule are created, according to both the bash shell output and Azure portal.
My config is below - details of some resources are omitted for brevity.
provider "azurerm" {
version = "~> 2.27.0"
features {}
}
resource "azurerm_resource_group" "rg" {
name = "${var.resource_group_name}-rg"
location = var.location
}
resource "azurerm_virtual_network" "vnet" {
resource_group_name = azurerm_resource_group.rg.name
name = "${var.prefix}Vnet"
address_space = ["10.0.0.0/16"]
location = var.location
}
resource "azurerm_subnet" "backend" {
resource_group_name = azurerm_resource_group.rg.name
name = "${var.prefix}backendSubnet"
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
service_endpoints = ["Microsoft.Sql"]
}
resource "azurerm_mariadb_server" "server" {
# DB server name can contain lower-case letters, numbers and dashes, NOTHING ELSE
resource_group_name = azurerm_resource_group.rg.name
name = "${var.prefix}-mariadb-server"
location = var.location
sku_name = "B_Gen5_2"
version = "10.3"
ssl_enforcement_enabled = true
}
resource "azurerm_mariadb_database" "mariadb_database" {
resource_group_name = azurerm_resource_group.rg.name
name = "${var.prefix}_mariadb_database"
server_name = azurerm_mariadb_server.server.name
charset = "utf8"
collation = "utf8_general_ci"
}
## Network Service Endpoint (add DB to subnet)
resource "azurerm_mariadb_virtual_network_rule" "vnet_rule" {
resource_group_name = azurerm_resource_group.rg.name
name = "${var.prefix}-mariadb-vnet-rule"
server_name = azurerm_mariadb_server.server.name
subnet_id = azurerm_subnet.backend.id
}
The issue looks to arise within 'func resourceArmMariaDbVirtualNetworkRuleCreateUpdate', but I don't know Go, so can't follow exactly what's causing it.
If anyone can see an issue, or knows how to get around this, please let me know!
Also, I'm not able to do it via the portal - step 3 here shows a section for configuring VNET rules, which is not present on my page for 'Azure database for mariaDB server'. I have the Global administrator role, so I don't think it's permissions-related.
From creating and manage Azure Database for MariaDB VNet service endpoints and VNet rules by using the Azure portal
The key point is that
Support for VNet service endpoints is only for General Purpose and
Memory Optimized servers.
So change the code sku_name = "B_Gen5_2" to sku_name = "GP_Gen5_2" or other eligible sku_name.
sku_name - (Required) Specifies the SKU Name for this MariaDB Server.
The name of the SKU, follows the tier + family + cores pattern (e.g.
B_Gen4_1, GP_Gen5_8). For more information see the product
documentation.
It takes a few minutes to deploy.

Use the existing Subnet details instead of creating again while creating Network Interface in Azure using terraform

I'm trying to create network Interface in Azure through terraform using below script :
resource "azurerm_subnet" "internal" {
name = "Subnet1"
resource_group_name = "${var.VNetResourceGroup}"
virtual_network_name = "${var.VNetName}"
address_prefix = "10.0.2.0/24"
}
resource "azurerm_network_interface" "main" {
name = "${var.prefix}-nic"
location = "${var.location}"
resource_group_name = "${var.resourceGroup}"
ip_configuration {
name = "ipconfig1"
subnet_id = "${azurerm_subnet.internal.id}"
private_ip_address_allocation = "dynamic"
}
}
This script creates the Subnet Subnet1 and assigning that Subnet.id in ipconfiguration.
But if I have to create another network interface again through another .tf file with the same Subnet1 , how to do I get the ${azurerm_subnet.internal.id} value again.
That is if the Subnet is already existing and I do not want to create it, how to I set those values and use them?
EDIT
I have figured out, in powershell this is the script used to determine Subnet id :
$vnetId= "[resourceId("VNetRG",'Microsoft.Network/virtualNetworks', "VNetName")]"
$subnetRef = "[concat($vnetId, '/subnets/', "Subnet1")]"
where VNetRG - resource group of VNet ,
VNetName - Name of VNet ,
Subnet1 - Name of Subnet.
Can anyone tell me what is the equivalent script in terraform?
Use a subnet data source:
data "azurerm_subnet" "subnet1" {
name = "Subnet1"
virtual_network_name = "${var.VNetName}"
resource_group_name = "${var.VNetResourceGroup}"
}
Then reference it in your NIC code with
subnet_id = "${data.azurerm_subnet.subnet1.id}"

Resources