Edit

Create a virtual network flow log by using Terraform

Virtual network flow logs record information about IP traffic that flows through a virtual network. In this article, you use Terraform to create a virtual network flow log and a dedicated storage account for an existing virtual network. For more information, see Virtual network flow logs overview.

The Terraform configuration uses the regional Network Watcher instance that Azure creates automatically when Network Watcher is enabled. You create the flow log and storage account in the Network Watcher resource group.

Prerequisites

  • An Azure account with an active subscription. Create an account for free.
  • Install and configure Terraform.
  • An existing virtual network. To create one, see Create a virtual network.
  • Network Watcher enabled in the virtual network's region. Network Watcher is enabled by default unless you explicitly disable it.
  • The Microsoft.Insights resource provider registered in your subscription. For more information, see Register the Insights provider.
  • Permissions to create a flow log and storage account in the resource group that contains the regional Network Watcher instance.

Review the Terraform configuration

You can find the sample configuration for this article in the Azure Terraform GitHub repository.

The configuration creates the following resources:

The configuration also uses the Network Watcher data source to reference the existing regional Network Watcher instance.

  1. Create a directory to test the sample Terraform configuration, and make it the current directory.

  2. Create a file named providers.tf, and insert the following code:

    terraform {
      required_version = ">= 1.5.0"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 5.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~> 3.7"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
  3. Create a file named main.tf, and insert the following code:

    data "azurerm_network_watcher" "network_watcher" {
      name                = var.network_watcher_name
      resource_group_name = var.network_watcher_resource_group_name
    }
    
    resource "random_string" "storage_account_suffix" {
      length  = 8
      lower   = true
      numeric = true
      special = false
      upper   = false
    }
    
    resource "azurerm_storage_account" "flow_logs" {
      name                     = "flowlogs${random_string.storage_account_suffix.result}"
      resource_group_name      = data.azurerm_network_watcher.network_watcher.resource_group_name
      location                 = data.azurerm_network_watcher.network_watcher.location
      account_tier             = "Standard"
      account_kind             = "StorageV2"
      account_replication_type = var.storage_account_replication_type
    }
    
    resource "azurerm_network_watcher_flow_log" "vnet_flow_log" {
      name                 = var.flow_log_name
      network_watcher_name = data.azurerm_network_watcher.network_watcher.name
      resource_group_name  = data.azurerm_network_watcher.network_watcher.resource_group_name
      location             = data.azurerm_network_watcher.network_watcher.location
    
      target_resource_id = var.virtual_network_id
      storage_account_id = azurerm_storage_account.flow_logs.id
      enabled            = true
      version            = var.flow_log_version
    
      retention_policy {
        enabled = var.retention_days > 0
        days    = var.retention_days
      }
    }
    
  4. Create a file named variables.tf, and insert the following code:

    variable "network_watcher_name" {
      type        = string
      default     = "NetworkWatcher_eastus"
      description = "Name of the existing regional Network Watcher instance."
    }
    
    variable "network_watcher_resource_group_name" {
      type        = string
      default     = "NetworkWatcherRG"
      description = "Name of the resource group that contains the Network Watcher instance."
    }
    
    variable "virtual_network_id" {
      type        = string
      description = "Resource ID of the existing virtual network to monitor."
    
      validation {
        condition     = can(regex("(?i)^/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft.Network/virtualNetworks/[^/]+$", var.virtual_network_id))
        error_message = "The virtual_network_id value must be a full Azure virtual network resource ID."
      }
    }
    
    variable "flow_log_name" {
      type        = string
      default     = "vnet-flow-log"
      description = "Name of the virtual network flow log."
    }
    
    variable "flow_log_version" {
      type        = number
      default     = 2
      description = "Flow log format version. Valid values are 1 and 2."
    
      validation {
        condition     = contains([1, 2], var.flow_log_version)
        error_message = "The flow_log_version value must be 1 or 2."
      }
    }
    
    variable "retention_days" {
      type        = number
      default     = 0
      description = "Number of days to retain flow log data. Use 0 to retain data indefinitely."
    
      validation {
        condition     = var.retention_days >= 0 && var.retention_days <= 365
        error_message = "The retention_days value must be between 0 and 365."
      }
    }
    
    variable "storage_account_replication_type" {
      type        = string
      default     = "LRS"
      description = "Replication type for the flow log storage account."
    
      validation {
        condition     = contains(["LRS", "GRS", "ZRS"], var.storage_account_replication_type)
        error_message = "The storage_account_replication_type value must be LRS, GRS, or ZRS."
      }
    }
    
  5. Create a file named outputs.tf, and insert the following code:

    output "flow_log_id" {
      description = "Resource ID of the virtual network flow log."
      value       = azurerm_network_watcher_flow_log.vnet_flow_log.id
    }
    
    output "flow_log_name" {
      description = "Name of the virtual network flow log."
      value       = azurerm_network_watcher_flow_log.vnet_flow_log.name
    }
    
    output "storage_account_name" {
      description = "Name of the storage account that contains the flow log data."
      value       = azurerm_storage_account.flow_logs.name
    }
    

Set the existing resource values

Create a file named terraform.tfvars. Replace the placeholders with values for your existing virtual network and regional Network Watcher instance.

network_watcher_name                = "NetworkWatcher_eastus"
network_watcher_resource_group_name = "NetworkWatcherRG"
virtual_network_id                  = "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Network/virtualNetworks/<virtual-network-name>"

The virtual network and Network Watcher instance must be in the same Azure region. If your Network Watcher instance uses a custom name or resource group, update both Network Watcher values.

Initialize Terraform

Run terraform init to initialize the Terraform deployment. This command downloads the Azure provider required to manage your Azure resources.

terraform init -upgrade

Key points:

  • The -upgrade parameter upgrades the necessary provider plugins to the newest version that complies with the configuration's version constraints.

Create a Terraform execution plan

Run terraform plan to create an execution plan.

terraform plan -out main.tfplan

Key points:

  • The terraform plan command creates an execution plan, but doesn't execute it. Instead, it determines what actions are necessary to create the configuration specified in your configuration files. This pattern allows you to verify whether the execution plan matches your expectations before making any changes to actual resources.
  • The optional -out parameter allows you to specify an output file for the plan. Using the -out parameter ensures that the plan you reviewed is exactly what is applied.

Apply the Terraform execution plan

Run terraform apply to apply the execution plan to your cloud infrastructure.

terraform apply main.tfplan

Key points:

  • The example terraform apply command assumes you previously ran terraform plan -out main.tfplan.
  • If you specified a different filename for the -out parameter, use that same filename in the call to terraform apply.
  • If you didn't use the -out parameter, call terraform apply without any parameters.

Verify the flow log

  1. Sign in to the Azure portal.

  2. Search for and select Network Watcher.

  3. Under Logs, select Flow logs.

  4. Confirm that the flow log is enabled and that its target resource is your virtual network.

You can also display the flow log and storage account names from the Terraform outputs:

terraform output flow_log_name
terraform output storage_account_name

Clean up resources

When you no longer need the resources created via Terraform, do the following steps:

  1. Run terraform plan and specify the destroy flag.

    terraform plan -destroy -out main.destroy.tfplan
    

    Key points:

    • The terraform plan command creates an execution plan, but doesn't execute it. Instead, it determines what actions are necessary to create the configuration specified in your configuration files. This pattern allows you to verify whether the execution plan matches your expectations before making any changes to actual resources.
    • The optional -out parameter allows you to specify an output file for the plan. Using the -out parameter ensures that the plan you reviewed is exactly what is applied.
  2. Run terraform apply to apply the execution plan.

    terraform apply main.destroy.tfplan
    

The destroy operation deletes the flow log and the storage account created by this configuration. It doesn't delete the existing virtual network or Network Watcher instance.

Troubleshoot Terraform on Azure

Troubleshoot common problems when using Terraform on Azure