TerraForm and Azure NFS-enabled Blob Storage
Continuing my journey of TerraForm with Microsoft’s Azure cloud, I needed to create some blob storage with NFS enabled (which currently has to be done at the storage account level and can only be turned on or off at account creation time).
This is slightly complicated because NFSv3 enabled storage accounts cannot allow public network access, preventing azurerm_storage_container from working, so we have to use an ARM template to deploy as a workaround.
Firstly the standard resource group definition:
resource "azurerm_resource_group" "storage" {
name = "RG-STORAGE-001"
location = "West Europe"
tags = merge(var.base_tags, var.tags_live)
}
Next, the storage account - note that you must set is_hns_enabled
to true and enable_https_traffic_only
to false in order to enable NFS support. The subnets were defined as in my previous post, these rules allow access to the storage account (including its NFS shares) only from the subnets listed:
resource "azurerm_storage_account" "system" {
name = "stasystem001"
resource_group_name = azurerm_resource_group.storage.name
location = azurerm_resource_group.storage.location
account_tier = "Standard"
account_replication_type = "ZRS"
min_tls_version = "TLS1_2"
is_hns_enabled = true
enable_https_traffic_only = false
nfsv3_enabled = true
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = [
azurerm_subnet.access.id,
azurerm_subnet.devtest.id,
azurerm_subnet.production.id
]
}
tags = merge(var.base_tags, var.tags_live)
}
One thing not covered by my previous posts are the private DNS zones for setting up private endpoint connections to various services correctly:
resource "azurerm_private_dns_zone" "blob_storage" {
name = "privatelink.blob.core.windows.net"
resource_group_name = azurerm_resource_group.network.name
tags = merge(var.base_tags, var.tags_live)
}
resource "azurerm_private_dns_zone" "file_storage" {
name = "privatelink.file.core.windows.net"
resource_group_name = azurerm_resource_group.network.name
tags = merge(var.base_tags, var.tags_live)
}
resource "azurerm_private_dns_zone" "mariadb" {
name = "privatelink.mariadb.database.azure.com"
resource_group_name = azurerm_resource_group.network.name
tags = merge(var.base_tags, var.tags_live)
}
resource "azurerm_private_dns_zone" "cosmos_mongo" {
name = "privatelink.mongo.cosmos.azure.com"
resource_group_name = azurerm_resource_group.network.name
tags = merge(var.base_tags, var.tags_live)
}
Next I create a private endpoint and enable Azure defender:
resource "azurerm_private_endpoint" "storage_system" {
name = "EP-HPC-STASYSTEM-001"
location = azurerm_resource_group.storage.location
resource_group_name = azurerm_resource_group.storage.name
subnet_id = azurerm_subnet.private.id
private_service_connection {
name = "pc-stasystem-001"
private_connection_resource_id = azurerm_storage_account.system.id
is_manual_connection = false
subresource_names = ["blob"]
}
private_dns_zone_group {
name = "stasystem001"
private_dns_zone_ids = [azurerm_private_dns_zone.blob_storage.id]
}
tags = merge(var.base_tags, var.tags_live)
}
resource "azurerm_advanced_threat_protection" "storage_system" {
target_resource_id = azurerm_storage_account.system.id
enabled = true
}
Finally the ARM template for a blob container:
resource "azurerm_template_deployment" "yum-storage" {
name = "yum-storage-deployment"
depends_on = [ azurerm_storage_account.system ]
resource_group_name = azurerm_resource_group.storage.name
deployment_mode = "Incremental"
parameters = {
storage_account_name = azurerm_storage_account.system.name
container_name = "yum-repos"
}
template_body = <<EOF
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storage_account_name": {
"type": "string"
},
"container_name": {
"type": "string"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"name": "[concat(parameters('storage_account_name'), '/default/', parameters('container_name'))]",
"apiVersion": "2021-02-01",
"properties": {"publicAccess": "None"}
}
]
}
EOF
}