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
- An Azure subscription. If you don’t have one already, create an Azure free account. Currently, with an Azure free account, you can try Azure Database for MySQL free for 12 months. For more information, see Use an Azure free account to try Azure Database for MySQL - Flexible Server for free.
- Installed and configured Terraform.
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.0ramkumarchan
Microsoft
Joined July 29, 2024
Azure Database for MySQL Blog
Follow this blog board to get notified when there's new activity