I recently had to do this and want to remember how to do it in the future. Well, I know it’s not durable but …
First, go to Azure Portal and into dev tools and run
Assuming you’re viewing all services via the URL:
https://portal.azure.com/#allservices/category/All
Then, in 2024 July 6 you were able to do use this JS code to extract a lot of stuff that’ll make you happy:
// Function to collect and combine all unique defs
function collectUniqueDefs() {
const uniqueDefs = new Map();
const defsElements = document.querySelectorAll('defs');
defsElements.forEach(defs => {
defs.childNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
uniqueDefs.set(node.id, node);
}
});
});
// Create a combined defs element
const combinedDefs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
uniqueDefs.forEach(node => {
combinedDefs.appendChild(node.cloneNode(true));
});
return combinedDefs.outerHTML;
}
// Function to get the full SVG markup based on the <use> href attribute
function getFullSvg(useElement, combinedDefs) {
if (!useElement) return null;
const href = useElement.getAttribute('href') || useElement.getAttribute('xlink:href');
const symbolId = href.startsWith('#') ? href.substring(1) : href;
const symbolElement = document.getElementById(symbolId);
if (symbolElement) {
// Create a new SVG element with the content of the symbol
const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
if (useElement.hasAttribute('height')) {
svgElement.setAttribute('height', useElement.getAttribute('height'));
}
if (useElement.hasAttribute('width')) {
svgElement.setAttribute('width', useElement.getAttribute('width'));
}
if (useElement.hasAttribute('aria-hidden')) {
svgElement.setAttribute('aria-hidden', useElement.getAttribute('aria-hidden'));
}
if (useElement.hasAttribute('role')) {
svgElement.setAttribute('role', useElement.getAttribute('role'));
}
if (useElement.hasAttribute('focusable')) {
svgElement.setAttribute('focusable', useElement.getAttribute('focusable'));
}
// Include the combined defs section once in the SVG
svgElement.innerHTML = combinedDefs + symbolElement.innerHTML;
// Return the outer HTML of the new SVG element
return svgElement.outerHTML;
}
return useElement.outerHTML; // Fallback to original <use> element if symbol not found
}
// Function to download data as a JSON file
function downloadJSON(data, filename) {
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Function to extract service names and their corresponding icons
function extractServiceIcons(batchSize = 50) {
const services = [];
const serviceItems = Array.from(document.querySelectorAll('.fxs-sidebar-flyout-allservices-list > li')); // List of all service items
let currentCategory = '';
// Collect the combined defs
const combinedDefs = collectUniqueDefs();
for (let i = 0; i < serviceItems.length; i += batchSize) {
const batch = serviceItems.slice(i, i + batchSize);
batch.forEach(item => {
if (item.classList.contains('fxs-sidebar-item-category')) {
// This is a category item
currentCategory = item.querySelector('h3').innerText.trim();
} else if (item.classList.contains('fxs-sidebar-flyout-item-content')) {
const serviceName = item.querySelector('.fxs-sidebar-label-name').innerText;
const useElement = item.querySelector('.fxs-sidebar-flyout-icon use');
const fullSvg = getFullSvg(useElement, combinedDefs); // Get the full SVG markup
services.push({
category: currentCategory,
name: serviceName,
icon: fullSvg
});
}
// Ignore items with 'fxs-sidebar-flyout-noresults' class or those with id starting with '_weave_e_'
});
// Download the batch as a JSON file
const batchData = JSON.stringify(services);
const batchNumber = i / batchSize + 1;
downloadJSON(batchData, `services_batch_${batchNumber}.json`);
services.length = 0; // Clear the array for the next batch
// Process in batches to avoid overload
if (i + batchSize < serviceItems.length) {
console.log(`Processed and downloaded batch ${batchNumber}`);
}
}
}
// Extract the service icons and names and download in parts
extractServiceIcons();
You may need to enable pasting code into the console when it tells you to be careful of doing this
allow pasting
Wait a bit — minutes, not seconds. And the files will come over to you one at a time. In my case they were numbered 1 thru 10, and the program below has that hardwired:
import os
import json
import re
# Function to clean up the SVG content
def clean_svg_content(svg_content):
return svg_content.replace('\\"', '"') if svg_content else ''
# Function to sanitize file names
def sanitize_filename(name):
# Remove special characters, truncate at the first hyphen, and remove extra spaces
sanitized = re.sub(r'[^\w\s-]', '', name)
sanitized = sanitized.split('-')[0].strip()
sanitized = re.sub(r'\s+', ' ', sanitized) # Remove extra spaces
return sanitized
# Create the directory structure
base_dir = 'AzureServicesIcons'
if not os.path.exists(base_dir):
os.makedirs(base_dir)
# List to hold master data
master_data = []
# Loop through the batch files
for i in range(1, 11):
batch_file = f'services_batch_{i}.json'
print(f"Processing {batch_file}...")
with open(batch_file, 'r') as file:
data = json.load(file)
for entry in data:
category = entry['category']
name = entry['name']
svg_content = entry.get('icon', None)
if svg_content is None:
print(f"Warning: No icon found for {name} in {category}")
continue
sanitized_name = sanitize_filename(name)
cleaned_svg_content = clean_svg_content(svg_content)
# Create category directory if it doesn't exist
category_dir = os.path.join(base_dir, category)
if not os.path.exists(category_dir):
os.makedirs(category_dir)
# Define the SVG file path
svg_file_path = os.path.join(category_dir, f"{sanitized_name}.svg")
# Write the SVG content to the file
with open(svg_file_path, 'w') as svg_file:
svg_file.write(cleaned_svg_content)
print(f"Saved SVG for {name} in {category}")
# Add entry to the master data
master_data.append({
'category': category,
'name': name,
'svg_file': svg_file_path
})
# Write the master JSON file
master_file = os.path.join(base_dir, 'master_data.json')
with open(master_file, 'w') as master_json_file:
json.dump(master_data, master_json_file, indent=4)
print(f"Master data written to {master_file}")
This should move all the icons into a directory with many subdirectories and a master JSON file to work with. Awesome, huh?
