Skip to content
This repository was archived by the owner on Oct 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/linters/PSScriptAnalyzerSettings.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@{

# Disable specific rules by name
ExcludeRules = @(
'PSUseShouldProcessForStateChangingFunctions',
'PSUseSingularNouns'
)
}
1 change: 1 addition & 0 deletions .github/workflows/code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
VALIDATE_MARKDOWN: true
VALIDATE_POWERSHELL: true
VALIDATE_YAML: true
POWERSHELL_CONFIG_FILE: PSScriptAnalyzerSettings.psd1
#YAMLLINT_CONFIG_FILE: .github/linters/.yamllint.yml
#VALIDATE_EDITORCONFIG: true
# Disable errors to only generate a report
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
3-CostInformation/*.csv
3-CostInformation/*.xls
3-CostInformation/*.xlsx
7-Report/*.csv
7-Report/*.xls
7-Report/*.xlsx

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
Expand Down
268 changes: 160 additions & 108 deletions 7-Report/Get-Report.ps1
Original file line number Diff line number Diff line change
@@ -1,135 +1,187 @@
<#
.SYNOPSIS
Exports Azure resource availability comparison between regions to Excel or CSV.
Exports Azure resource availability and cost comparison between regions to Excel

.DESCRIPTION
Reads the output from Get-AvailabilityInformation.ps1, structures it, and
exports to an Excel or CSV file, including SKU details.
Reads the output from 2-AvailabilityCheck/Get-Region.ps1 and 3-CostInformation/Perform-RegionComparison.ps1, structures it, and
exports to an Excel file, including SKU details.

.PARAMETER InputPath
Path to the JSON or CSV file containing availability information.

.PARAMETER OutputPath
Path where the report should be saved (without extension).

.PARAMETER ExportExcel
If specified, exports to .xlsx (requires ImportExcel module), otherwise .csv.
.PARAMETER availabilityInfoPath
Array of paths to JSON files containing availability information.
.PARAMETER costComparisonPath
Path to the JSON file containing cost comparison information.
#>

param(
[Parameter(Mandatory = $true)]
[string]$InputPath
[Parameter(Mandatory = $false)][array]$availabilityInfoPath,
[Parameter(Mandatory = $false)][string]$costComparisonPath
)

# Import data
try {
if ($InputPath.EndsWith(".json")) {
$rawdata = Get-Content $InputPath | ConvertFrom-Json
} elseif ($InputPath.EndsWith(".csv")) {
$rawdata = Import-Csv $InputPath
} else {
throw "Unsupported input format. Please provide a JSON or CSV file."
Function Set-ColumnColor {
param(
[Parameter(Mandatory = $true)] [object]$startColumn,
[Parameter(Mandatory = $true)] [string]$cellValPositive,
[Parameter(Mandatory = $true)] [string]$cellValNegative
)
$colCount = $ws.Dimension.End.Column
for ($col = $startColumn; $col -le $colCount; $col++) {
$colLetter = [OfficeOpenXml.ExcelCellAddress]::GetColumnLetter($col)
$cell = $ws.Cells["$colLetter$row"]
if ($cell.Value -eq $cellValPositive) {
$cell.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$cell.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::LightGreen)
}
elseif ($cell.Value -eq $cellValNegative) {
$cell.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$cell.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::LightCoral)
}
}
} catch {
Write-Error "Failed to read input data: $_"
exit 1
}

# Initialize an array to collect output
$reportData = @()

# Process each item in the JSON
foreach ($item in $rawdata) {
# if implementedSkus is exists and is not null
if ($item.ImplementedSkus -and $item.ImplementedSkus.Count -gt 0) {
$implementedSkus = ($item.ImplementedSkus | ForEach-Object {
# Return the SKU name based on the resource type
if ($item.ResourceType -eq "microsoft.compute/disks") {
$_.name
} elseif ($item.ResourceType -eq "microsoft.compute/virtualmachines") {
$_.vmSize
} elseif ($item.ResourceType -eq "microsoft.keyvault/vaults") {
$_.name + " (Family: " + $_.family + ")"
} elseif ($item.ResourceType -eq "microsoft.network/applicationgateways") {
$_.name + " (Family: " + $_.family + ")"
} elseif ($item.ResourceType -eq "microsoft.network/publicipaddresses") {
$_.name + " (" + $_.tier + ")"
} elseif ($item.ResourceType -eq "microsoft.operationalinsights/workspaces") {
$_.name + " (Last Sku Update: " + $_.lastSkuUpdate + ")"
} elseif ($item.ResourceType -eq "microsoft.recoveryservices/vaults") {
$_.name + " (" + $_.tier + ")"
} elseif ($item.ResourceType -eq "microsoft.sql/servers/databases") {
$_.name + " (Capacity: " + $_.capacity + ")"
} elseif ($item.ResourceType -eq "microsoft.storage/storageaccounts") {
$_.name
} else {
# No action for other resource types
}
}) -join ", "
} else {
$implementedSkus = "N/A"
Function New-Worksheet {
param (
[Parameter(Mandatory = $true)][string]$WorksheetName,
[Parameter(Mandatory = $true)][int]$LastColumnNumber,
[Parameter(Mandatory = $true)][array]$reportData,
[Parameter(Mandatory = $false)][int]$startColumnNumber,
[Parameter(Mandatory = $false)][string]$cellValPositive,
[Parameter(Mandatory = $false)][string]$cellValNegative
)
$excelParams = @{
Path = $xlsxFileName
WorksheetName = $WorksheetName
AutoSize = $true
TableStyle = 'None'
PassThru = $true
}

$reportItem = [PSCustomObject]@{
ResourceType = $item.ResourceType
ResourceCount = $item.ResourceCount
ImplementedRegions = ($item.ImplementedRegions -join ", ")
ImplementedSkus = $implementedSkus
SelectedRegion = $item.SelectedRegion.region
IsAvailable = $item.SelectedRegion.available
$excelPkg = $reportData | Select-Object -Property $allProps | Export-Excel @excelParams
$ws = $excelPkg.Workbook.Worksheets[$WorksheetName]
$lastColLetter = [OfficeOpenXml.ExcelCellAddress]::GetColumnLetter($lastColumnNumber)
$headerRange = $ws.Cells["A1:$lastColLetter`1"]
$headerRange.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$headerRange.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::RoyalBlue)
$headerRange.Style.Font.Color.SetColor([System.Drawing.Color]::White)
for ($row = 2; $row -le ($reportData.Count + 1); $row++) {
# Call the function to set column colors based on cell values
If($startColumnNumber) {
Set-ColumnColor -startColumn $startColumnNumber -cellValPositive $cellValPositive -cellValNegative $cellValNegative
}
}
$excelPkg.Save()
"Sheet '$WorksheetName' with $($reportData.Count) entries added to '$xlsxFileName'."
}

$reportData += $reportItem
# Collect all property names in first-seen order
Function Get-Props {
param (
[array]$data
)
$allProps = @()
foreach ($obj in $data) {
foreach ($p in $obj.PSObject.Properties.Name) {
if ($allProps -notcontains $p) {
$allProps += $p
}
}
}
return $allProps
}

# Define output file name with current timestamp (yyyyMMdd_HHmmss)
#Define output file name with current timestamp (yyyyMMdd_HHmmss)
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$csvFileName = "Availability_Report_$timestamp.csv"
$xlsxFileName = "Availability_Report_$timestamp.xlsx"

$excelParams = @{
Path = $xlsxFileName
WorksheetName = "General"
AutoSize = $true
TableStyle = 'None'
PassThru = $true
}

# Export to CSV
$reportData | Export-Csv -Path $csvFileName -NoTypeInformation

# Make the Excel first row (header) with blue background and white text
$excelParams = @{
Path = $xlsxFileName
WorksheetName = "General"
AutoSize = $true
TableStyle = 'None'
PassThru = $true
If ($availabilityInfoPath) {
# Consider splitting into functions for better readability and maintainability
$reportData = @()
foreach ($path in $availabilityInfoPath) {
$rawdata = Get-Content $path | ConvertFrom-Json -Depth 10
foreach ($item in $rawdata) {
$implementedSkus = ""
# if implementedSkus is exists and is not null
if ($item.ImplementedSkus -and $item.ImplementedSkus.Count -gt 0) {
$resourceType = $item.ResourceType
ForEach ($sku in $item.ImplementedSkus) {
# Customize output based on ResourceType
switch ($resourceType) {
"microsoft.compute/virtualmachines" { $implementedSkus += $sku.vmSize + "," }
default { $implementedSkus += $sku.name + "," }
}
}
}
else {
$implementedSkus += "N/A"
}
$implementedSkus = $implementedSkus.TrimEnd(",")
$regionAvailability = "Not available"
$regionHeader = $item.SelectedRegion.region
If ($item.SelectedRegion.available -eq "true") {
$regionAvailability = "Available"
}
# If an object with this resource type already exists in reportData, update it
if ($reportData | Where-Object { $_.ResourceType -eq $item.ResourceType }) {
# If it exists, update the existing object with the new region availability
$existingItem = $reportData | Where-Object { $_.ResourceType -eq $item.ResourceType }
$existingItem | Add-Member -MemberType NoteProperty -Name $regionHeader -Value $regionAvailability
}
else {
$reportItem = [PSCustomObject]@{
ResourceType = $item.ResourceType
ResourceCount = $item.ResourceCount
ImplementedRegions = ($item.ImplementedRegions -join ", ")
ImplementedSkus = $implementedSkus
$regionHeader = $regionAvailability
}
$reportData += $reportItem
}
}
}
$WorksheetName = "ServiceAvailability"
$allProps = Get-Props -data $reportData
$lastColumnNumber = $allProps.Count
New-Worksheet -WorksheetName $WorksheetName -LastColumnNumber $lastColumnNumber -reportData $reportData -startColumnNumber 5 -cellValPositive "Available" -cellValNegative "Not available"
}

if (Get-Module -ListAvailable -Name ImportExcel) {
$excelPkg = $reportData | Export-Excel @excelParams
$ws = $excelPkg.Workbook.Worksheets["General"]
if ($reportData -and $reportData.Count -gt 0 -and $reportData[0]) {
$lastColLetter = [OfficeOpenXml.ExcelCellAddress]::GetColumnLetter(6)
$headerRange = $ws.Cells["A1:$lastColLetter`1"]
$headerRange.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$headerRange.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::RoyalBlue)
$headerRange.Style.Font.Color.SetColor([System.Drawing.Color]::White)

# Set background color for IsAvailable column based on value
for ($row = 2; $row -le ($reportData.Count + 1); $row++) {
$cell = $ws.Cells["F$row"]
if ($cell.Value -eq $true) {
$cell.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$cell.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::LightGreen)
} elseif ($cell.Value -eq $false) {
$cell.Style.Fill.PatternType = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
$cell.Style.Fill.BackgroundColor.SetColor([System.Drawing.Color]::LightCoral)
If ($costComparisonPath) {
$rawdata = Get-Content $costComparisonPath | ConvertFrom-Json -Depth 10
$costReportData = @()
$uniqueMeterIds = $rawdata | Select-Object -Property OrigMeterId -Unique
foreach ($meterId in $uniqueMeterIds) {
$meterId = $meterId.OrigMeterId
# get all occurrences of this meterId in $rawdata
$meterOccurrences = $rawdata | Where-Object { $_.OrigMeterId -eq $meterId }
$basedata = $meterOccurrences | Select-Object -Property ServiceName, MeterName, ProductName, SKUName -Unique
$serviceName = $basedata.ServiceName
$meterName = $basedata.MeterName
$productName = $basedata.ProductName
$skuName = $basedata.SKUName
$pricingObj = [PSCustomObject]@{}
foreach ($occurrence in $meterOccurrences) {
$region = $occurrence.Region
if ($null -eq $region -or $region -eq "") {
$region = "Global"
}
$retailPrice = $occurrence.RetailPrice
$priceDiffToOrigin = $occurrence.PriceDiffToOrigin
$pricingObj | Add-Member -MemberType NoteProperty -Name "$region-RetailPrice" -Value $retailPrice
$pricingObj | Add-Member -MemberType NoteProperty -Name "$region-PriceDiffToOrigin" -Value $priceDiffToOrigin
}
# Create a new object for each unique meter ID
$costReportItem = [PSCustomObject]@{
MeterId = $meterId
ServiceName = $serviceName
MeterName = $meterName
ProductName = $productName
SKUName = $skuName
}
Foreach ($key in $pricingObj.PSObject.Properties.Name) {
$costReportItem | Add-Member -MemberType NoteProperty -Name $key -Value $pricingObj.$key
}
# Add the cost report item to the report data array
$costReportData += $costReportItem
}
$excelPkg.Save()
} else {
Write-Warning "Excel export skipped. 'ImportExcel' module not found. Install with: Install-Module -Name ImportExcel"
$WorksheetName = "CostComparison"
$allProps = Get-Props -data $costReportData
$lastColumnNumber = $allProps.Count
New-Worksheet -WorksheetName $WorksheetName -LastColumnNumber $lastColumnNumber -reportData $costReportData
}
36 changes: 26 additions & 10 deletions 7-Report/readme.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
# Export Script

This script generates formatted Excel (`.xlsx`) and CSV reports based on the output from the previous check script. The reports provide detailed information for each service, including:
This script generates formatted Excel (`.xlsx`)reports based on the output from the previous check script. The reports provide detailed information for each service, including:

## Service Availability Report

- **Resource type**
- **Resource count**
- **Implemented (origin) regions**
- **Implemented SKUs**
- **Selected (target) region**
- **Availability in the selected region**
- **Selected (target) regions**
- **Availability in the selected regions**

## Cost Comparison Report

- **Azure Cost Meter ID**
- **Service Name**
- **Meter Name**
- **Product Name**
- **SKU Name**
- **Retail Price per region**
- **Price Difference to origin region per region**

These reports help you analyze service compatibility across different regions.
These reports help you analyze service compatibility and cost differences across different regions.

## Usage
## Dependencies

- This script requires the `ImportExcel` PowerShell module.
- The script requires you to have run either the `2-AvailabilityCheck/Get-Region.ps1` or `3-CostInformation/Perform-RegionComparison.ps1` or both scripts to generate the necessary JSON input files for availability and cost data.

## Usage Instructions

1. Open a PowerShell command line.
2. Navigate to the `7-Export` folder.
3. Run the script:
2. Navigate to the `7-Report` folder.
3. If you have created one or more availability JSON files using the `2-AvailabilityCheck/Get-Region.ps1` script, run the following commands, replacing the path with your actual file path(s):

```powershell
.\Get-Report.ps1 -InputPath "..\2-AvailabilityCheck\Availability_Mapping_Asia_Pacific.json"
.\Get-Report.ps1 -availabilityInfoPath `@("..\2-AvailabilityCheck\Availability_Mapping_Asia_Pacific.json", "..\2-AvailabilityCheck\Availability_Mapping_Europe.json")` -costComparisonPath "..\3-CostInformation\region_comparison_prices.json"
```

The script generates `.xlsx` and `.csv` files in the `7-Export` folder, named `Availability_Report_CURRENTTIMESTAMP`.
The script generates an `.xlsx` and `.csv` files in the `7-report` folder, named `Availability_Report_CURRENTTIMESTAMP`.