Blog Post

Azure Database for MySQL Blog
6 MIN READ

Using Terraform to deploy Azure Database for MySQL with Private Endpoint

ramkumarchan's avatar
ramkumarchan
Icon for Microsoft rankMicrosoft
Apr 24, 2025

This blog post describes how to use Terraform to deploy an Azure Database for MySQL flexible server with a private endpoint in a virtual network.

Prerequisites

Before beginning this process, be sure that you have

Step 1 - Implement the Terraform code

To implement the Terraform code, perform the following steps

1. Create a directory that you can use to test the sample Terraform code. Make the Terraform directory the current directory.

2. Create a file named providers.tf, and insert the following code, replacing the <subscription_id> with the appropriate value:

terraform {
  required_version = ">=1.10"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>4.26.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~>3.7.1"
    }
  }
}
provider "azurerm" {
  subscription_id = "<subscription_id>"
  features {}
}

3. Create a file named main.tf, and insert the following code:

# Generate random resource group name
resource "random_pet" "rg_name" {
  prefix = var.resource_group_name_prefix
}

resource "azurerm_resource_group" "rg" {
  location = var.resource_group_location
  name     = random_pet.rg_name.id
}

# Generate random value for the name
resource "random_string" "name" {
  length  = 8
  lower   = true
  numeric = false
  special = false
  upper   = false
}

# Generate random value for the login password
resource "random_password" "password" {
  length           = 8
  lower            = true
  min_lower        = 1
  min_numeric      = 1
  min_special      = 1
  min_upper        = 1
  numeric          = true
  override_special = "_"
  special          = true
  upper            = true
}

# Manages the Virtual Network
resource "azurerm_virtual_network" "default" {
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  name                = "vnet-${random_string.name.result}"
  resource_group_name = azurerm_resource_group.rg.name
}

# Manages the Subnet
resource "azurerm_subnet" "default" {
  address_prefixes     = ["10.0.2.0/24"]
  name                 = "subnet-${random_string.name.result}"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.default.name
}

# Enables you to manage Private DNS zones within Azure DNS
resource "azurerm_private_dns_zone" "default" {
  name                = "privatelink.mysql.database.azure.com"
  resource_group_name = azurerm_resource_group.rg.name
}

# Enables you to manage Private DNS zone Virtual Network Links
resource "azurerm_private_dns_zone_virtual_network_link" "default" {
  name                  = "mysqlfsVnetZone${random_string.name.result}"
  private_dns_zone_name = azurerm_private_dns_zone.default.name
  resource_group_name   = azurerm_resource_group.rg.name
  virtual_network_id    = azurerm_virtual_network.default.id

  depends_on = [azurerm_subnet.default]
}

# Manages the MySQL Flexible Server
resource "azurerm_mysql_flexible_server" "default" {
  location                     = azurerm_resource_group.rg.location
  name                         = "mysqlfs-${random_string.name.result}"
  resource_group_name          = azurerm_resource_group.rg.name
  administrator_login          = random_string.name.result
  administrator_password       = random_password.password.result
  backup_retention_days        = 7
  geo_redundant_backup_enabled = false
  sku_name                     = "GP_Standard_D2ds_v4"
  version                      = "8.0.21"
  public_network_access        = "Disabled"

  high_availability {
    mode                      = "SameZone"
  }
  maintenance_window {
    day_of_week  = 0
    start_hour   = 8
    start_minute = 0
  }
  storage {
    iops    = 360
    size_gb = 20
  }

  depends_on = [azurerm_private_dns_zone_virtual_network_link.default]
}

# Create private endpoint for MySQL server
resource "azurerm_private_endpoint" "default" {
  name                = "private-endpoint-mysql"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  subnet_id           = azurerm_subnet.default.id

  private_service_connection {
    name                           = "private-serviceconnection1"
    private_connection_resource_id = azurerm_mysql_flexible_server.default.id
    subresource_names              = ["mysqlServer"]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = "dns-zone-group1"
    private_dns_zone_ids = [azurerm_private_dns_zone.default.id]
  }
}

# Create the MySQL Flexible Server Database
resource "azurerm_mysql_flexible_database" "database" {
  charset             = "utf8mb4"
  collation           = "utf8mb4_unicode_ci"
  name                = "mysqlfsdb"
  resource_group_name = azurerm_resource_group.rg.name
  server_name         = azurerm_mysql_flexible_server.default.name
  depends_on = [azurerm_mysql_flexible_server.default]
}

If you require public connectivity based on firewall rules in addition to the Private Endpoint, you can enable PublicNetworkAccess and add firewall rules by making the following code modifications in main.tf.

  • Set PublicNetworkAccess to Enabled in azurerm_mysql_flexible_server.
public_network_access = "Enabled"
  • Add the following code snippet at the end of main.tf to add firewall rules:
# Add firewall rules to MySQL Flexible Server
resource "azurerm_mysql_flexible_server_firewall_rule" "rule1" {
  name                = "AllowIPRange"
  resource_group_name = azurerm_resource_group.rg.name
  server_name         = azurerm_mysql_flexible_server.default.name
  start_ip_address    = "40.112.0.0"
  end_ip_address      = "40.112.255.255"
  depends_on = [azurerm_mysql_flexible_server.default]
}

 

4. Create a file named variables.tf, and insert the following code:

variable "resource_group_location" {
  type        = string
  default     = "westeurope"
  description = "Location of the resource group."
}

variable "resource_group_name_prefix" {
  type        = string
  default     = "mysql-fs-db-rg"
  description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}

 

5. Create a file named outputs.tf, and insert the following code:

output "azurerm_mysql_flexible_server" {
  value = azurerm_mysql_flexible_server.default.name
}

output "admin_login" {
  value = azurerm_mysql_flexible_server.default.administrator_login
}

output "admin_password" {
  sensitive = true
  value     = azurerm_mysql_flexible_server.default.administrator_password
}

output "mysql_flexible_server_database_name" {
  value = azurerm_mysql_flexible_database.database.name
}

output "resource_group_name" {
  value = azurerm_resource_group.rg.name
}

Step 2 - Initialize Terraform

To initialize the Terraform deployment, run the following command which downloads the Azure provider required to manage your Azure resources.

terraform init -upgrade

Note: Running the terraform init command with the -upgrade parameter upgrades the necessary provider plugins to the newest version that complies with the configuration's version constraints.

Step 3 - Create a Terraform execution plan

To create an execution plan, run the following command:

terraform plan -out main.tfplan

Important: The terraform plan command creates an execution plan but doesn't run it. Instead, it determines the actions 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.

Step 4 - Apply a Terraform execution plan

To apply the execution plan to your cloud infrastructure, run the following command:

terraform apply main.tfplan

Note: 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, then call terraform apply without any parameters.

Step 5 - Verify the results

To display the Azure Database for MySQL - Flexible Server database, run the following command:

az mysql flexible-server db show \
    --resource-group <resource_group_name> \
    --server-name <azurerm_mysql_flexible_server> \
    --database-name <mysql_flexible_server_database_name>

Note: The az mysql flexible-server db show command shows the details of the database created using terraform. Replace the values for <resource_group_name>, <azurerm_mysql_flexible_server>, and <mysql_flexible_server_database_name> from the terraform apply output. You can also run the terraform output command to view these values.

Clean up resources

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

1. Run terraform plan and specify the destroy flag.

terraform plan -destroy -out main.destroy.tfplan

Note: 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

Troubleshoot Terraform on Azure

If you need to troubleshoot using Terraform on Azure, see the article Troubleshoot common problems for using Terraform on Azure.

Summary

You should now have all the information you need to create an Azure Database for MySQL flexible server with Private Endpoint using Terraform. If you have any queries or suggestions, please let us know by leaving a comment below or by contacting directly us at AskAzureDBforMySQL@service.microsoft.com.

Please provide any product-related feedback in the Azure Database for MySQL Community.

Updated Apr 16, 2025
Version 1.0
No CommentsBe the first to comment