azコマンドでザッと見るのはともかく、やっぱりExcelになってないと何かと不便だなぁ、ということで書いた。GitHubに放流しといた。それっぽいツールがあるのかも知れないけど、探すのが面倒。
#!/usr/bin/env python3.11
# built-in modules
import argparse
import configparser
import os
import pathlib
import sys
import warnings
cur_python = f"python{sys.version_info[0]}.{sys.version_info[1]}"
# exit with redden message
def exit_with_error(msg) -> None:
sys.exit("\033[31m" + msg + "\033[0m")
# external modules
try:
from azure.identity import DefaultAzureCredential
except ModuleNotFoundError:
exit_with_error(f"Please install 'azure-ai-translation-text' before executing.\n{cur_python} -m pip install azure-identity")
try:
from azure.mgmt.resource import ResourceManagementClient
except ModuleNotFoundError:
exit_with_error(f"Please install 'azure-ai-translation-text' before executing.\n{cur_python} -m pip install azure-mgmt-resource")
try:
import openpyxl as xl
from openpyxl.worksheet.table import Table, TableStyleInfo
from openpyxl.styles import Color, Font, PatternFill, DEFAULT_FONT
except ModuleNotFoundError:
exit_with_error(f"Please install 'openpyxl' before executing.\n{cur_python} -m pip install openpyxl")
try:
from azure.mgmt.postgresqlflexibleservers import PostgreSQLManagementClient
except ModuleNotFoundError:
exit_with_error(f"Please install 'azure-mgmt-postgresqlflexibleservers' before executing.\n{cur_python} -m pip install azure-mgmt-postgresqlflexibleservers")
try:
from azure.mgmt.mysqlflexibleservers import MySQLManagementClient
except ModuleNotFoundError:
exit_with_error(f"Please install 'azure-mgmt-mysqlflexibleservers' before executing.\n{cur_python} -m pip install azure-mgmt-mysqlflexibleservers")
# suppress warnings from openpyxl
# "Workbook contains no default style, apply openpyxl's default"
warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl')
def get_sub_id() -> str:
try:
return os.environ["AZURE_SUBSCRIPTION_ID"]
except KeyError:
print("Could not find subscription ID in environment variables.\nTrying to read from configuration file...")
config = configparser.ConfigParser()
try:
default_path = "~/.azure/clouds.config"
config.read(pathlib.Path(default_path).expanduser())
return config["AzureCloud"]["subscription"]
except KeyError:
print(f"Could not find subscription ID in configuration file, '{default_path}'.")
exit_with_error("Please set 'AZURE_SUBSCRIPTION' environment variable or login with 'az login' first.")
def list_to_excel(rgname: str) -> None:
client_map = {
"Microsoft.DBforPostgreSQL/flexibleServers": PostgreSQLManagementClient,
"Microsoft.DBforMySQL/flexibleServers": MySQLManagementClient
}
credential = DefaultAzureCredential()
sub_id = get_sub_id()
resource_client = ResourceManagementClient(credential, sub_id)
resource_list = resource_client.resources.list_by_resource_group(rgname)
# Create a workbook
wb = xl.Workbook()
ws = wb.worksheets[0]
ws.title = "Resources"
# Header
labels = ['id', 'name', 'type', 'location', 'extended_location', 'tags', 'plan', 'properties', 'kind', 'managed_by', 'sku-name', 'sku-tier', 'sku-size', 'sku-family', 'sku-model', 'sku-capacity', 'version', 'identity', 'created_time', 'changed_time', 'provisioning_state', 'systemData-createdBy', 'systemData-createdByType', 'systemData-createdAt', 'systemData-lastModifiedBy', 'systemData-lastModifiedByType']
for i, label in enumerate(labels, 1):
ws.cell(row = 1, column = i).value = label
ws.row_dimensions[1].height = 16
k_sku = "sku"
k_version = "version"
k_systemData = "systemData"
row_num = 2
for r in list(resource_list):
ws.row_dimensions[row_num].height = 16
for k in labels:
if k[0:len(k_sku)] == k_sku:
try:
ws.cell(row = row_num, column = labels.index(k) + 1).value = getattr(r.sku, k[len(k_sku) + 1:])
except AttributeError:
pass
elif k[0:len(k_systemData)] == k_systemData:
if len(r.additional_properties) == 1:
try:
ws.cell(row = row_num, column = labels.index(k) + 1).value = r.additional_properties[k_systemData][k[len(k_systemData) + 1:]]
except KeyError:
pass
elif k[0:len(k_version)] == k_version:
client_class = client_map.get(r.type)
if client_class:
flex_client = client_class(credential, sub_id)
for item in flex_client.servers.list_by_resource_group(resource_group_name=rgname):
try:
ws.cell(row = row_num, column = labels.index(k) + 1).value = f"{item.version}.{item.minor_version}"
except AttributeError:
ws.cell(row = row_num, column = labels.index(k) + 1).value = f"{item.version}"
else:
try:
ws.cell(row = row_num, column = labels.index(k) + 1).value = getattr(r, k)
except ValueError:
pass
row_num += 1
# It's hack to set default font
_font = Font(name = "Calibri", size = 11)
{k: setattr(DEFAULT_FONT, k, v) for k, v in _font.__dict__.items()}
# Set table
table = Table(displayName = "Table1", ref = f"A1:Z{row_num - 1}")
table.tableStyleInfo = TableStyleInfo(name = "TableStyleMedium2", showRowStripes = True)
ws.add_table(table)
# Save
import datetime
today = datetime.datetime.now()
path = f"All Resources Under '{rgname}' - " + today.strftime("%Y-%m-%d %H-%M-%S") + ".xlsx"
wb.save(path)
# On macOS, the workbook will be opened automatically
if sys.platform == "darwin":
import subprocess
coms = ["open", path]
subprocess.run(coms)
def main() -> None:
parser = argparse.ArgumentParser(description = "List all the resources in a resource group and put the list in a Excel file.")
parser.add_argument("resource_group", help = "Resource group name to list the resources")
args = parser.parse_args()
list_to_excel(args.resource_group)
if __name__ == "__main__":
if sys.platform != "darwin":
print("This script is intended to be run on macOS.")
main()
こんな感じのExcelに出力される。PostgreSQLとMySQLだけ、バージョンが出るw