From fb57c67603701d7110c3e105a3aa12e1c23aa14c Mon Sep 17 00:00:00 2001
From: Timna Brown <24630902+brown9804@users.noreply.github.com>
Date: Tue, 13 May 2025 10:08:36 -0600
Subject: [PATCH 1/5] comment
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 0410d29..66a8e17 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,8 @@ Last updated: 2025-05-13
## How to enable IOPS (script)
+> [Automate the activation of Autoscale IOPS using Python and the Azure REST API](./autoscaleMultiple-IOPS/), ideal for managing multiple servers across resource groups or entire subscriptions. This feature is only supported on General Purpose and Business Critical tiers, and currently not available via Azure CLI or PowerShell.
+
## How to Monitor IOPS Scaling
> How I know if the IOPS have scaled up or down when the server's using the autoscale IOPS feature? You can use the `metrics available in Azure Monitor`.
From c9e7790ab2aa3f48ba0dedfb1eaf0cc53f97962b Mon Sep 17 00:00:00 2001
From: Timna Brown <24630902+brown9804@users.noreply.github.com>
Date: Tue, 13 May 2025 10:11:06 -0600
Subject: [PATCH 2/5] adjusting path
---
autoscaleMultiple-IOPS/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/autoscaleMultiple-IOPS/README.md b/autoscaleMultiple-IOPS/README.md
index 5aa5782..f595ecc 100644
--- a/autoscaleMultiple-IOPS/README.md
+++ b/autoscaleMultiple-IOPS/README.md
@@ -51,7 +51,7 @@ Review [the script](./scripts/enable_autoscale_iops_byRG.py), and download it to
> - For each server, retrieving its resource group.
> - Applying the update if the server is in a supported tier (General Purpose or Business Critical).
-Review [the script](./scripts/enable_autoscale_iops.py), and download it to your local machine.
+Review [the script](./scripts/enable_autoscale_iops_across_subscription.py), and download it to your local machine.
> Example: enabling Autoscale IOPS on 4 different servers, each hosted in different resource group and same subscription.
From 4901b06944b9086712d8474a84a5dfd6f4377f22 Mon Sep 17 00:00:00 2001
From: Timna Brown <24630902+brown9804@users.noreply.github.com>
Date: Tue, 13 May 2025 10:34:16 -0600
Subject: [PATCH 3/5] visual guidance
---
autoscaleMultiple-IOPS/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/autoscaleMultiple-IOPS/README.md b/autoscaleMultiple-IOPS/README.md
index f595ecc..5a8d4bd 100644
--- a/autoscaleMultiple-IOPS/README.md
+++ b/autoscaleMultiple-IOPS/README.md
@@ -53,9 +53,9 @@ Review [the script](./scripts/enable_autoscale_iops_byRG.py), and download it to
Review [the script](./scripts/enable_autoscale_iops_across_subscription.py), and download it to your local machine.
-> Example: enabling Autoscale IOPS on 4 different servers, each hosted in different resource group and same subscription.
+> Example: enabling Autoscale IOPS on different servers, each hosted in different resource group and same subscription.
-
+https://github.com/user-attachments/assets/7c06f457-d1c5-4277-ab1f-cee6621b6871
## How to execute it Script to Enable Autoscale IOPS
From 468f91a505325d7cd64e8f4af1a301a84fdc7e8c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Tue, 13 May 2025 16:34:33 +0000
Subject: [PATCH 4/5] Fix Markdown syntax issues
---
autoscaleMultiple-IOPS/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/autoscaleMultiple-IOPS/README.md b/autoscaleMultiple-IOPS/README.md
index 5a8d4bd..1a994a2 100644
--- a/autoscaleMultiple-IOPS/README.md
+++ b/autoscaleMultiple-IOPS/README.md
@@ -55,7 +55,7 @@ Review [the script](./scripts/enable_autoscale_iops_across_subscription.py), and
> Example: enabling Autoscale IOPS on different servers, each hosted in different resource group and same subscription.
-https://github.com/user-attachments/assets/7c06f457-d1c5-4277-ab1f-cee6621b6871
+
## How to execute it Script to Enable Autoscale IOPS
From 6c953e70022d0d6c1e56ef570d3c348bdbb15e9c Mon Sep 17 00:00:00 2001
From: Timna Brown <24630902+brown9804@users.noreply.github.com>
Date: Tue, 13 May 2025 10:35:19 -0600
Subject: [PATCH 5/5] script across subs
---
...able_autoscale_iops_across_subscription.py | 151 ++++++++++++++++++
1 file changed, 151 insertions(+)
create mode 100644 autoscaleMultiple-IOPS/scripts/enable_autoscale_iops_across_subscription.py
diff --git a/autoscaleMultiple-IOPS/scripts/enable_autoscale_iops_across_subscription.py b/autoscaleMultiple-IOPS/scripts/enable_autoscale_iops_across_subscription.py
new file mode 100644
index 0000000..e51539b
--- /dev/null
+++ b/autoscaleMultiple-IOPS/scripts/enable_autoscale_iops_across_subscription.py
@@ -0,0 +1,151 @@
+"""
+Azure MySQL Flexible Server Auto IOPS Enabler
+
+This script connects to Azure using the Azure CLI and Azure REST API to:
+1. Retrieve the current subscription ID.
+2. List all Resource Groups in the current subscription.
+3. List all MySQL Flexible Servers in each resource group.
+4. Skip resource groups with no MySQL Flexible Servers.
+5. Check if each server is eligible for Auto IOPS scaling (only GeneralPurpose or BusinessCritical tiers).
+6. Optionally enable Auto IOPS scaling if not already enabled.
+7. Print a summary of which servers were updated or skipped.
+"""
+
+import subprocess
+import requests
+import shutil
+from azure.identity import AzureCliCredential
+
+SUPPORTED_TIERS = {"GeneralPurpose", "BusinessCritical"}
+
+def mask_value(value, start=4, end=4):
+ return value[:start] + '*' * (len(value) - start - end) + value[-end:]
+
+def get_subscription_id():
+ az_path = shutil.which("az")
+ if az_path is None:
+ raise FileNotFoundError("Azure CLI (az) not found in system PATH.")
+ result = subprocess.check_output([az_path, "account", "show", "--query", "id", "-o", "tsv"], text=True)
+ return result.strip()
+
+def get_access_token():
+ credential = AzureCliCredential()
+ token = credential.get_token("https://management.azure.com/.default")
+ return token.token
+
+def list_resource_groups(subscription_id, access_token):
+ url = f"https://management.azure.com/subscriptions/{subscription_id}/resourcegroups?api-version=2024-06-01-preview"
+ headers = {"Authorization": f"Bearer {access_token}"}
+ response = requests.get(url, headers=headers)
+ response.raise_for_status()
+ return response.json()
+
+def list_mysql_servers(subscription_id, resource_group, access_token):
+ url = (
+ f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/"
+ f"{resource_group}/providers/Microsoft.DBforMySQL/flexibleServers?api-version=2024-06-01-preview"
+ )
+ headers = {"Authorization": f"Bearer {access_token}"}
+ response = requests.get(url, headers=headers)
+ if response.status_code == 404:
+ return {"value": []}
+ response.raise_for_status()
+ return response.json()
+
+def get_server_config(subscription_id, resource_group, server_name, access_token):
+ url = (
+ f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}/"
+ f"providers/Microsoft.DBforMySQL/flexibleServers/{server_name}?api-version=2024-06-01-preview"
+ )
+ headers = {"Authorization": f"Bearer {access_token}"}
+ response = requests.get(url, headers=headers)
+ response.raise_for_status()
+ return response.json()
+
+def enable_auto_io_scaling_put(subscription_id, resource_group, server_name, access_token):
+ url = (
+ f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}/"
+ f"providers/Microsoft.DBforMySQL/flexibleServers/{server_name}?api-version=2024-06-01-preview"
+ )
+ headers = {
+ "Authorization": f"Bearer {access_token}",
+ "Content-Type": "application/json"
+ }
+ payload = {
+ "properties": {
+ "storage": {
+ "autoIoScaling": "Enabled"
+ }
+ }
+ }
+ response = requests.patch(url, headers=headers, json=payload)
+ print(f"PATCH Response: {response.status_code} - {response.text}")
+ response.raise_for_status()
+ return response.json()
+
+def main():
+ try:
+ print("Script started")
+ subscription_id = get_subscription_id()
+ print(f"Using subscription: {mask_value(subscription_id)}")
+
+ access_token = get_access_token()
+ print(f"Access Token: {mask_value(access_token[:10])}...")
+
+ resource_groups = list_resource_groups(subscription_id, access_token)
+ eligible_servers = []
+ ineligible_servers = []
+
+ for rg in resource_groups.get("value", []):
+ rg_name = rg["name"]
+ servers = list_mysql_servers(subscription_id, rg_name, access_token).get("value", [])
+
+ if not servers:
+ print(f"[SKIP] No MySQL Flexible Servers found in resource group: {rg_name}")
+ continue
+
+ print(f"\nMySQL Flexible Servers in {rg_name}:")
+ for server in servers:
+ print(f" - {server['name']}")
+
+ target_server = input(f"Enter a specific server name in {rg_name} (or press Enter to apply to all): ").strip()
+
+ for server in servers:
+ server_name = server["name"]
+ if target_server and server_name.lower() != target_server.lower():
+ continue
+
+ config = get_server_config(subscription_id, rg_name, server_name, access_token)
+ tier = config["sku"]["tier"]
+ auto_io = config["properties"]["storage"].get("autoIoScaling", "Disabled")
+
+ if tier not in SUPPORTED_TIERS:
+ ineligible_servers.append((server_name, tier))
+ continue
+
+ if auto_io == "Enabled":
+ print(f"[SKIP] Auto IOPS already enabled for {server_name}")
+ continue
+
+ print(f"[UPDATE] Enabling Auto IOPS for {server_name} (Tier: {tier})...")
+ enable_auto_io_scaling_put(subscription_id, rg_name, server_name, access_token)
+ eligible_servers.append(server_name)
+
+ print("\nSummary:")
+ if eligible_servers:
+ print("Auto IOPS enabled for:")
+ for name in eligible_servers:
+ print(f" - {name}")
+ else:
+ print("No servers were updated.")
+
+ if ineligible_servers:
+ print("\nIneligible servers (unsupported tier):")
+ for name, tier in ineligible_servers:
+ print(f" - {name} (Tier: {tier})")
+
+ except Exception as e:
+ print(f"Error: {e}")
+
+if __name__ == "__main__":
+ main()