Merge pull request #1394 from anosov1960/PAYG-transition

Added a resource name parameter and increased KQL limit
This commit is contained in:
Alexander (Sasha) Nosov
2025-05-27 17:17:50 -07:00
committed by GitHub
3 changed files with 422 additions and 176 deletions
@@ -88,42 +88,77 @@ param (
[switch] $ReportOnly,
[Parameter (Mandatory= $false)]
[switch] $UseManagedIdentity
[switch] $UseManagedIdentity,
[Parameter (Mandatory= $false)]
[int] $batchSize = 500
)
$scriptStartTime = Get-Date
Write-Output "Script execution started at: $($scriptStartTime.ToString('yyyy-MM-dd HH:mm:ss'))"
function Connect-Azure {
[CmdletBinding()]
param(
[Parameter (Mandatory= $true)]
[string] $TenantId,
[Parameter(Mandatory=$false)]
[string] $TenantId = $null,
[Parameter (Mandatory= $false)]
[switch]$UseManagedIdentity
[Parameter(Mandatory=$false)]
[switch] $UseManagedIdentity
)
# 1) Detect environment
$envType = "Local"
if ($env:AZUREPS_HOST_ENVIRONMENT -and $env:AZUREPS_HOST_ENVIRONMENT -like 'cloud-shell*') {
$envType = "CloudShell"
# 1) Detect host environment
$envType = 'Local'
if ($env:AZUREPS_HOST_ENVIRONMENT -like 'cloud-shell*') {
$envType = 'CloudShell'
}
elseif (($env:AZUREPS_HOST_ENVIRONMENT -and $env:AZUREPS_HOST_ENVIRONMENT -like 'AzureAutomation*') -or $PSPrivateMetadata.JobId) {
$envType = "AzureAutomation"
$UseManagedIdentity=$true
elseif (($env:AZUREPS_HOST_ENVIRONMENT -like 'AzureAutomation*') -or $PSPrivateMetadata.JobId) {
$envType = 'AzureAutomation'
$UseManagedIdentity = $true
}
Write-Verbose "Environment detected: $envType"
Write-Output "Environment detected: $envType"
# 2) Ensure Az.PowerShell context
Write-Output "Not connected to Azure PowerShell. Running Connect-AzAccount..."
if ($UseManagedIdentity -or $envType -eq 'AzureAutomation') {
$ctx = Connect-AzAccount -Tenant $TenantId -Identity -ErrorAction Stop | Out-Null
$currentCtx = Get-AzContext -ErrorAction SilentlyContinue
if ($currentCtx -and $currentCtx.Account) {
if ($TenantId) {
if ($currentCtx.Tenant.Id -eq $TenantId) {
Write-Output "Already in Az tenant $TenantId"
}
else {
Write-Output "Switching Az context to tenant $TenantId without re-authentication"
$newContext = Set-AzContext -Tenant $TenantId -ErrorAction SilentlyContinue
if($null -eq $newContext -or $newContext.TenantId -ne $TenantId)
{
Connect-AzAccount -Tenant $TenantId | Out-Null
}
}
}
else {
Write-Output "Using existing Az context: Tenant $($currentCtx.Tenant.Id)"
}
}
else {
$ctx = Connect-AzAccount -Tenant $TenantId -ErrorAction Stop | Out-Null
Write-Output "Not connected to Az PowerShell—authenticating..."
if ($UseManagedIdentity) {
if ($TenantId) {
Connect-AzAccount -Identity -Tenant $TenantId | Out-Null
}
else {
Connect-AzAccount -Identity -ErrorAction Stop | Out-Null
}
}
else {
if ($TenantId) {
Connect-AzAccount -Tenant $TenantId | Out-Null
}
else {
Connect-AzAccount | Out-Null
}
}
$ctx = Get-AzContext
Write-Output "Connected to Az PowerShell as: $($ctx.Account) in tenant $($ctx.Tenant.Id)"
}
Write-Output "Connected to Azure PowerShell as: $($ctx.Account)"
}
@@ -138,45 +173,34 @@ if($null -ne $ExclusionTags){
}
}
}
# Ensure connection with both PowerShell and CLI.
if($UseManagedIdentity -or $envType -eq 'AzureAutomation') {
if ($TenantId) {
Connect-Azure -TenantId $TenantId -UseManagedIdentity $UseManagedIdentity
} else {
Connect-Azure -UseManagedIdentity $UseManagedIdentity
}
} else {
if ($TenantId) {
Connect-Azure -TenantId $TenantId
} else {
Connect-Azure
}
}
$context = Get-AzContext -ErrorAction SilentlyContinue
Write-Output "Connected to Azure as: $($context.Account)"
if (-not $TenantId) {
$TenantId = (Get-AzContext).Tenant.Id
$TenantId = $context.Tenant.Id
Write-Output "No TenantId provided. Using current context TenantId: $TenantId"
} else {
Write-Output "Using provided TenantId: $TenantId"
}
# Ensure connection with both PowerShell and CLI.
if ($UseManagedIdentity) {
Connect-Azure ($TenantId, $UseManagedIdentity)
}else{
Connect-Azure ($TenantId)
}
# Ensure the required modules are imported
# Ensure NuGet provider is available
if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) {
Install-PackageProvider -Name NuGet -Force
}
# Check if Az module is installed
$installedModule = Get-InstalledModule -Name Az -ErrorAction SilentlyContinue
if (-not $installedModule) {
Write-Host "Az module not found. Installing latest version..."
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
} else {
# Get the latest version available in the PSGallery
$latestVersion = (Find-Module -Name Az -Repository PSGallery).Version
if ($installedModule.Version -lt $latestVersion) {
Write-Host "Az module is outdated. Updating to latest version..."
Update-Module -Name Az -Force
} else {
Write-Host "Az module is already up to date. No action needed."
}
}
try{
Import-Module Az.Accounts
}catch{
@@ -200,10 +224,10 @@ $modifiedResources = @()
if ($SubId -like "*.csv") {
$subscriptions = Import-Csv $SubId
}elseif($SubId -ne "") {
$subscriptions = Get-AzSubscription -SubscriptionId $SubId | Sort-Object TenantId
Write-Output "Passed Subscription $($SubId)"
$subscriptions = Get-AzSubscription -SubscriptionId $SubId
}else {
$subscriptions = Get-AzSubscription | Where-Object { $_.TenantId -eq $tenantId }
$subscriptions = Get-AzSubscription | Where-Object { $_.TenantId -eq $tenantId }
}
# Handle MachineName input (single or CSV)
@@ -258,7 +282,7 @@ foreach ($sub in $subscriptions) {
$query += "
| extend machineId = tolower(tostring(id))
| project machineId, machineName = name
| project machineId, machineName = tolower(name)
| join kind= inner (
resources
| where subscriptionId =~ '$($sub.Id)'
@@ -269,27 +293,38 @@ foreach ($sub in $subscriptions) {
| extend extensionName = name
| extend extensionPublisher = properties.publisher
| extend extensionType = properties.type
| parse id with '/subscriptions/' subscriptionId '/resourceGroups/' resourceGroup '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' extensionName
) on `$left.machineName == `$right.machineName
| parse id with '/subscriptions/' subscriptionId '/resourceGroups/' resourceGroup '/providers/Microsoft.HybridCompute/machines/' machineNameRaw '/extensions/' extensionName
| extend machineName = tolower(machineNameRaw)
) on `$left.machineName == `$right.machineName
| project machineName, extensionName, resourceGroup, location, subscriptionId, extensionPublisher, extensionType
"
| order by machineName asc"
$skipToken = $null
#Write-Output $query
Write-Output $query
$resources = Search-AzGraph -Query "$($query)"
Write-Output "Found $($resources.Count) resource(s) to update"
$count = $resources.Count
$allResults = [System.Collections.Generic.List[PSObject]]::new()
do{
$resources = Search-AzGraph -Query "$($query)" -First $batchSize -SkipToken $skipToken
$allResults.AddRange($resources)
$skipToken = $resources.SkipToken
}while($skipToken)
$count = $allResults.Count
while($count -gt 0) {
$count-=1
$setID = @{
MachineName = $resources[$count].MachineName
Name = $resources[$count].extensionName
ResourceGroup = $resources[$count].resourceGroup
Location = $resources[$count].location
SubscriptionId = $resources[$count].subscriptionId
Publisher = $resources[$count].extensionPublisher
ExtensionType = $resources[$count].extensionType
MachineName = $allResults[$count].MachineName
Name = $allResults[$count].extensionName
ResourceGroup = $allResults[$count].resourceGroup
Location = $allResults[$count].location
SubscriptionId = $allResults[$count].subscriptionId
Publisher = $allResults[$count].extensionPublisher
ExtensionType = $allResults[$count].extensionType
}
write-Output " MachineName - $($setID.MachineName)"
@@ -407,6 +442,7 @@ foreach ($sub in $subscriptions) {
Write-Output "ReportOnly mode enabled. Skipping modification for: $($setID.MachineName)"
}
}
}
}
}
@@ -421,3 +457,8 @@ if ($modifiedResources.Count -gt 0) {
}
write-Output "Arc SQL Update Script completed"
$scriptEndTime = Get-Date
$executionDuration = $scriptEndTime - $scriptStartTime
Write-Output "Script execution ended at: $($scriptEndTime.ToString('yyyy-MM-dd HH:mm:ss'))"
Write-Output "Total execution time: $($executionDuration.ToString('hh\:mm\:ss'))"
@@ -57,9 +57,10 @@ The scripts is seamlessly integrated with Azure Authentication. It uses managed
| **Parameter**                                         | **Value**                                                                       | **Description** |
|:--|:--|:--|
|-SubId|`subscription_id` *or* a file_name|Optional: Subscription id or a .csv file with the list of subscriptions<sup>1</sup>. If not specified all subscriptions will be scanned|
|-ResourceGroup |`resource_group_name`|Optional: Limits the scope to a specific resource group|
|-LicenseType | `LicenseIncluded` (default) or `BasePrice` | Optional: Sets the license type to the specified value |
|`-SubId`|`subscription_id` *or* a file_name|Optional: Subscription id or a .csv file with the list of subscriptions<sup>1</sup>. If not specified all subscriptions will be scanned|
|`-ResourceGroup` |`resource_group_name`|Optional: Limits the scope to a specific resource group|
|`-ResourceName` |`resource_name`|Optional: Limits the scope to resouyrces associated with this name. For SQL Server - updates all databases under the specified server. For SQL Managed Instance - updates the specified instance. For SQL VM - updates the specified VM |
|`-LicenseType` | `LicenseIncluded` (default) or `BasePrice` | Optional: Sets the license type to the specified value |
|`-ExclusionTags`| `{"name":"value","name":"value"}` |*Optional*. If specified, excludes the resources that have this tag assigned.|
|`-TenantId`| `tenant_id` |*Optional*. If specified, uses this tenant id to log in. Otherwise, the current context is used.|
|`-ReportOnly`| |*Optional*. If true, generates a csv file with the list of resources that are to be modified, but doesn't make the actual change.|
@@ -31,22 +31,27 @@
Optional. If specified, excludes the resources that have this tag assigned.
.PARAMETER TenantId
Optional. If specified, this tenant id to log in both PoaerShell and CLI. Otyherwise, the current logoin context is used.
Optional. If specified, this tenant id to log in both PowerShell and CLI. Otherwise, the current login context is used.
.PARAMETER ReportOnly
Optional. If true, generates a csv file with the list of resources that are to be modified, but doesn't make the actual change.
.PARAMETER UseManagedIdentity
Optional. If true, logs in both PoaerShell and CLI using managed identity. Required to run the script as a runbook.
Optional. If true, logs in both PowerShell and CLI using managed identity. Required to run the script as a runbook.
.PARAMETER ResourceName
Optional. If specified, only updates resources related to this name:
- For SQL Server: Updates all databases under the specified server
- For SQL Managed Instance: Updates the specified instance
- For SQL VM: Updates the specified VM
#>
param (
[Parameter(Mandatory = $false)]
[string] $SubId="$null",
[string] $SubId,
[Parameter(Mandatory = $false)]
[string] $ResourceGroup=$null,
[string] $ResourceGroup,
[Parameter(Mandatory = $false)]
[ValidateSet("LicenseIncluded", "BasePrice", IgnoreCase = $false)]
@@ -62,9 +67,16 @@ param (
[switch] $ReportOnly,
[Parameter (Mandatory= $false)]
[switch] $UseManagedIdentity
[switch] $UseManagedIdentity,
[Parameter (Mandatory= $false)]
[string] $ResourceName
)
$scriptStartTime = Get-Date
Write-Output "Script execution started at: $($scriptStartTime.ToString('yyyy-MM-dd HH:mm:ss'))"
# Suppress unnecessary logging output
$VerbosePreference = "SilentlyContinue"
$DebugPreference = "SilentlyContinue"
@@ -115,7 +127,6 @@ function Connect-Azure {
$acct = az account show --output json | ConvertFrom-Json
}
Write-Output "Azure CLI logged in as: $($acct.user.name)"
}
@@ -218,7 +229,7 @@ if($tagTable.Keys.Count -gt 0) {
$tagsFilter += " && "
$tagcount = $tagTable.Keys.Count
foreach ($tag in $tagTable.Keys) {
$tagcount --
$tagcount--
$tagsFilter += " tags.$($tag) != '$($tagTable[$tag])' "
if($tagcount -gt 0) {
$tagsFilter += " && "
@@ -248,12 +259,26 @@ foreach ($sub in $subscriptions) {
# --- Section: Update SQL Virtual Machines ---
try {
Write-Output "Seeking SQL Virtual Machines that require a license update to $SqlVmLicenseType..."
$sqlVmQuery = if ($rgFilter) {
"[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' && $rgFilter $tagsFilter].{name:name, resourceGroup:resourceGroup, sqlServerLicenseType:sqlServerLicenseType, type:type, id:id, Location:location}"
} else {
"[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' $tagsFilter].{name:name, resourceGroup:resourceGroup, sqlServerLicenseType:sqlServerLicenseType, type:type, id:id, Location:location}"
# Build SQL VM query
$sqlVmQuery = "[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR'"
# Add resource group filter if specified
if ($rgFilter) {
$sqlVmQuery += " && $rgFilter"
}
# Add name filter if ResourceName specified
if ($ResourceName) {
$sqlVmQuery += " && name=='$ResourceName'"
}
# Add tags filter if specified
if ($tagsFilter) {
$sqlVmQuery += " $tagsFilter"
}
$sqlVmQuery += "].{name:name, resourceGroup:resourceGroup, sqlServerLicenseType:sqlServerLicenseType, type:type, id:id, Location:location}"
Write-Output "Seeking SQL Virtual Machines with filter $sqlVmQuery..."
$sqlVMs = az sql vm list --query $sqlVmQuery -o json | ConvertFrom-Json
@@ -265,7 +290,7 @@ foreach ($sub in $subscriptions) {
}
foreach ($sqlvm in $sqlVMs) {
if($null -ne (az vm list --query "[?name=='$sqlvm.name' && resourceGroup=='$sqlvm.resourceGroup' $tagsFilter]"))
if($null -ne (az vm list --query "[?name=='$($sqlvm.name)' && resourceGroup=='$($sqlvm.resourceGroup)' $tagsFilter]"))
{
$vmStatus = az vm get-instance-view --resource-group $sqlvm.resourceGroup --name $sqlvm.name --query "{Name:name, ResourceGroup:resourceGroup, PowerState:instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus | [0]}" -o json | ConvertFrom-Json
if ($vmStatus.PowerState -eq "VM running") {
@@ -292,7 +317,7 @@ foreach ($sub in $subscriptions) {
}
}
else {
Write-Output "SQL VM '$($sqlvm.name)' in RG '$($sqlvm.resourceGroup)' Skipping because of tags ($tags)..."
Write-Output "SQL VM '$($sqlvm.name)' in RG '$($sqlvm.resourceGroup)' Skipping because of tags..."
}
}
if($sqlVmsToUpdate.Count -eq 0) {
@@ -310,12 +335,21 @@ foreach ($sub in $subscriptions) {
try {
Write-Output "Processing SQL Managed Instances that are running to $LicenseType..."
# Build Managed Instance query
$miRunningQuery = "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state=='Ready'"
# Add resource group filter if specified
if ($rgFilter) {
$miRunningQuery += " && $rgFilter"
}
# Add name filter if ResourceName specified
if ($ResourceName) {
$miRunningQuery += " && name=='$ResourceName'"
}
# Add tags filter if specified
if ($tagsFilter) {
$miRunningQuery += " $tagsFilter"
}
@@ -337,7 +371,7 @@ foreach ($sub in $subscriptions) {
SubID = ($mi.id -split '/')[2]
ResourceName = $mi.name
ResourceType = $mi.ResourceType
Status = $mi.State
Status = $mi.state
OriginalLicenseType = $mi.licenseType
ResourceGroup = $mi.resourceGroup
Location = $mi.location
@@ -357,87 +391,247 @@ foreach ($sub in $subscriptions) {
# --- Section: Update SQL Databases and Elastic Pools ---
try {
Write-Output "Querying SQL Servers within this subscription..."
$serverQuery = if ($rgFilter) { "[?$rgFilter]" } else { "[]" }
$servers = az sql server list --query $serverQuery -o json | ConvertFrom-Json
foreach ($server in $servers) {
# Update SQL Databases
Write-Output "Scanning SQL Databases on server '$($server.name)'..."
$dbs = az sql db list --resource-group $server.resourceGroup --server $server.name --query "[?licenseType!='$($LicenseType)' && licenseType!=null $tagsFilter].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}" -o json | ConvertFrom-Json
if( $dbs.Count -eq 0) {
Write-Output "No SQL Databases found on Server $($server.name) that require a license update."
} else {
Write-Output "Found $($dbs.Count) SQL Databases on Server $($server.name) that require a license update."
# First, let's verify we're in the right subscription context
$currentSubContext = az account show --query id -o tsv
Write-Output "Currently in subscription context: $currentSubContext"
if ($currentSubContext -ne $sub.id) {
Write-Output "Subscription context mismatch! Re-setting context..."
az account set --subscription $sub.id
}
# Build SQL Server query with proper JMESPath syntax
$serverQuery = ""
$filterAdded = $false
# Start with an empty filter array
if ($rgFilter -or $ResourceName -or $tagsFilter) {
$serverQuery = "["
# Add resource group filter if specified
if ($rgFilter) {
$serverQuery += "?$rgFilter"
$filterAdded = $true
}
foreach ($db in $dbs) {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($db.id -split '/')[2]
ResourceName = $db.name
ResourceType = $db.ResourceType
Status = $db.State
OriginalLicenseType = $db.licenseType
ResourceGroup = $db.resourceGroup
Location = $db.location
}
if (-not $ReportOnly) {
Write-Output "Updating SQL Database '$($db.name)' on server '$($server.name)' to license type '$LicenseType'..."
$result = az sql db update --name $db.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType -o json | ConvertFrom-Json
$finalStatus += $result
}
}
# Update Elastic Pools
try {
Write-Output "Scanning Elastic Pools on server '$($server.name)'..."
$elasticPools = az sql elastic-pool list --resource-group $server.resourceGroup --server $server.name --query "[?licenseType!='$($LicenseType)' && licenseType!=null $tagsFilter].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}" --only-show-errors -o json | ConvertFrom-Json
if( $elasticPools.Count -eq 0) {
Write-Output "No Elastic Pools found on Server $($server.name) that require a license update."
# Add name filter if ResourceName is provided
if ($ResourceName) {
if ($filterAdded) {
$serverQuery += " && name=='$ResourceName'"
} else {
Write-Output "Found $($elasticPools.Count) Elastic Pools on Server $($server.name) that require a license update."
}
foreach ($pool in $elasticPools) {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($pool.id -split '/')[2]
ResourceName = $pool.name
ResourceType = $pool.ResoureType
Status = $pool.State
OriginalLicenseType = $pool.licenseType
ResourceGroup = $pool.resourceGroup
Location = $pool.location
}
if (-not $ReportOnly) {
Write-Output "Updating Elastic Pool '$($pool.name)' on server '$($server.name)' to license type '$LicenseType'..."
$result = az sql elastic-pool update --name $pool.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType --only-show-errors -o json | ConvertFrom-Json -ErrorAction SilentlyContinue
$finalStatus += $result
$report["ElasticPoolUpdated"] += $pool.name
}
$serverQuery += "?name=='$ResourceName'"
$filterAdded = $true
}
}
catch {
Write-Output "Encountered an issue while updating Elastic Pools on server '$($server.name)'. Continuing..."
# Add tag filter if specified
if ($tagsFilter -and $filterAdded) {
$serverQuery += "$tagsFilter"
} elseif ($tagsFilter) {
$serverQuery += "?1==1$tagsFilter" # A trick to make the tags filter work when it's the only filter
}
$serverQuery += "]"
} else {
# No filters, get all servers
$serverQuery = "[]"
}
# Output the query for debugging
Write-Output "SQL Server query: $serverQuery"
# Get all servers first as a fallback in case the query fails
$allServers = az sql server list -o json | ConvertFrom-Json
Write-Output "Found a total of $($allServers.Count) SQL Servers in subscription"
# Now try the filtered query
$servers = az sql server list --query "$serverQuery" -o json | ConvertFrom-Json
# Verify if we got any results
if ($null -eq $servers -or $servers.Count -eq 0) {
Write-Output "WARNING: No SQL Servers found with the specified filters."
Write-Output "Available SQL Servers in subscription:"
$allServers | ForEach-Object {
Write-Output " - $($_.name) (Resource Group: $($_.resourceGroup))"
}
# Use all servers if no specific resource name was provided
if (-not $ResourceName) {
Write-Output "Proceeding with all SQL Servers since no specific ResourceName was provided."
$servers = $allServers
}
} else {
Write-Output "Found $($servers.Count) SQL Servers matching the criteria."
$servers | ForEach-Object {
Write-Output " - $($_.name) (Resource Group: $($_.resourceGroup))"
}
}
}
catch {
# Process each server
foreach ($server in $servers) {
# Update SQL Databases
Write-Output "Scanning SQL Databases on server '$($server.name)' in resource group '$($server.resourceGroup)'..."
# First get all databases to check if any exist
$allDbs = az sql db list --resource-group $server.resourceGroup --server $server.name -o json | ConvertFrom-Json
Write-Output "Found a total of $($allDbs.Count) databases on server '$($server.name)'"
# Build database query with better error handling
$dbQuery = "[?licenseType!=null && licenseType!='$($LicenseType)'"
# Add tags filter if specified
if ($tagsFilter) {
$dbQuery += " $tagsFilter"
}
$dbQuery += "].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}"
Write-Output "Database query: $dbQuery"
# Get databases with error handling
try {
$dbs = az sql db list --resource-group $server.resourceGroup --server $server.name --query "$dbQuery" -o json | ConvertFrom-Json
if ($null -eq $dbs) {
Write-Output "No SQL Databases found on Server $($server.name) that require a license update."
} elseif ($dbs.Count -eq 0) {
Write-Output "No SQL Databases found on Server $($server.name) that require a license update."
} else {
Write-Output "Found $($dbs.Count) SQL Databases on Server $($server.name) that require a license update:"
$dbs | ForEach-Object {
Write-Output " - $($_.name) (Current license: $($_.licenseType))"
}
foreach ($db in $dbs) {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($db.id -split '/')[2]
ResourceName = $db.name
ResourceType = $db.ResourceType
Status = $db.State
OriginalLicenseType = $db.licenseType
ResourceGroup = $db.resourceGroup
Location = $db.location
}
if (-not $ReportOnly) {
Write-Output "Updating SQL Database '$($db.name)' on server '$($server.name)' to license type '$LicenseType'..."
try {
$result = az sql db update --name $db.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType -o json | ConvertFrom-Json
if ($result) {
Write-Output "Successfully updated database '$($db.name)' license to '$LicenseType'"
$finalStatus += $result
} else {
Write-Error "Failed to update database '$($db.name)' license. No result returned."
}
} catch {
Write-Error "Error updating database '$($db.name)': $_"
}
}
}
}
} catch {
Write-Error "Error querying databases on server '$($server.name)': $_"
}
# Update Elastic Pools with similar improved error handling
try {
Write-Output "Scanning Elastic Pools on server '$($server.name)'..."
# First check if there are any elastic pools
$allPools = az sql elastic-pool list --resource-group $server.resourceGroup --server $server.name --only-show-errors -o json 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($null -eq $allPools -or $allPools.Count -eq 0) {
Write-Output "No Elastic Pools found on server '$($server.name)'."
} else {
Write-Output "Found $($allPools.Count) total Elastic Pools on server '$($server.name)'."
# Build elastic pool query with better formatting
$elasticPoolQuery = "[?licenseType!=null && licenseType!='$($LicenseType)'"
# Add tags filter if specified
if ($tagsFilter) {
$elasticPoolQuery += " $tagsFilter"
}
$elasticPoolQuery += "].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:state}"
Write-Output "Elastic Pool query: $elasticPoolQuery"
$elasticPools = az sql elastic-pool list --resource-group $server.resourceGroup --server $server.name --query "$elasticPoolQuery" --only-show-errors -o json 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($null -eq $elasticPools -or $elasticPools.Count -eq 0) {
Write-Output "No Elastic Pools found on Server $($server.name) that require a license update."
} else {
Write-Output "Found $($elasticPools.Count) Elastic Pools on Server $($server.name) that require a license update:"
$elasticPools | ForEach-Object {
Write-Output " - $($_.name) (Current license: $($_.licenseType))"
}
foreach ($pool in $elasticPools) {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($pool.id -split '/')[2]
ResourceName = $pool.name
ResourceType = $pool.ResourceType
Status = $pool.State
OriginalLicenseType = $pool.licenseType
ResourceGroup = $pool.resourceGroup
Location = $pool.location
}
if (-not $ReportOnly) {
Write-Output "Updating Elastic Pool '$($pool.name)' on server '$($server.name)' to license type '$LicenseType'..."
try {
$result = az sql elastic-pool update --name $pool.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType --only-show-errors -o json 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($result) {
Write-Output "Successfully updated elastic pool '$($pool.name)' license to '$LicenseType'"
$finalStatus += $result
} else {
Write-Error "Failed to update elastic pool '$($pool.name)' license. No result returned."
}
} catch {
Write-Error "Error updating elastic pool '$($pool.name)': $_"
}
}
}
}
}
} catch {
Write-Error "Error processing Elastic Pools on server '$($server.name)': $_"
}
}
} catch {
Write-Error "An error occurred while processing SQL Databases or Elastic Pools: $_"
}
# --- Section: Update SQL Instance Pools ---
try {
Write-Output "Searching for SQL Instance Pools that require a license update..."
$instancePoolsQuery = if ($rgFilter) {
"[?licenseType!='${LicenseType}' && $rgFilter $tagsFilter].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}"
} else {
"[?licenseType!='${LicenseType}' $tagsFilter].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}"
# Build instance pool query
$instancePoolsQuery = "[?licenseType!='${LicenseType}'"
# Add resource group filter if specified
if ($rgFilter) {
$instancePoolsQuery += " && $rgFilter"
}
# Add name filter if ResourceName specified
if ($ResourceName) {
$instancePoolsQuery += " && name=='$ResourceName'"
}
# Add tags filter if specified
if ($tagsFilter) {
$instancePoolsQuery += " $tagsFilter"
}
$instancePoolsQuery += "].{name:name, licenseType:licenseType, location:location, resourceGroup:resourceGroup, id:id, ResourceType:type, State:status}"
$instancePools = az sql instance-pool list --query $instancePoolsQuery -o json 2>$null | ConvertFrom-Json
$poolsToUpdate = $instancePools | Where-Object { $_.licenseType -ne $LicenseType }
if($poolsToUpdate.Count -eq 0) {
@@ -452,7 +646,7 @@ foreach ($sub in $subscriptions) {
TenantID = $TenantId
SubID = ($pool.id -split '/')[2]
ResourceName = $pool.name
ResourceType = $pool.ResoureType
ResourceType = $pool.ResourceType
Status = $pool.State
OriginalLicenseType = $pool.licenseType
ResourceGroup = $pool.resourceGroup
@@ -473,25 +667,30 @@ foreach ($sub in $subscriptions) {
try {
Write-Output "Processing DataFactory SSIS Integration Runtime resources..."
Get-AzDataFactoryV2 | Where-Object { $_.ProvisioningState -eq "Succeeded" } | ForEach-Object {
Get-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $_.ResourceGroupName -DataFactoryName $_.DataFactoryName | Where-Object { $_.Type -eq "Managed" -and $_.State -ne "Starting" } | ForEach-Object {
if ($_.LicenseType -ne $LicenseType) {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($_.Id -split '/')[2]
ResourceName = $_.Name
ResourceType = "Microsoft.DataFactory/factories/integrationRuntimes"
Status = $_.State
OriginalLicenseType = $_.LicenseType
ResourceGroup = $_.ResourceGroupName
Location = $_.Location
}
# Update the license type to $LicenseType.
if (-not $ReportOnly) {
$result = Set-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $_.ResourceGroupName -DataFactoryName $_.DataFactoryName -Name $_.Name -LicenseType $LicenseType -Force
$finalStatus += $result
Write-Host ([Environment]::NewLine + "-- DataFactory '$($_.DataFactoryName)' integration runtime updated to license type $LicenseType")
}
$df = $_
Get-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $df.ResourceGroupName -DataFactoryName $df.DataFactoryName |
Where-Object {
$_.Type -eq "Managed" -and
$_.State -ne "Starting" -and
$_.LicenseType -ne $LicenseType -and
($null -eq $ResourceName -or $_.Name -eq $ResourceName)
} | ForEach-Object {
# Collect data before modification
$modifiedResources += [PSCustomObject]@{
TenantID = $TenantId
SubID = ($_.Id -split '/')[2]
ResourceName = $_.Name
ResourceType = "Microsoft.DataFactory/factories/integrationRuntimes"
Status = $_.State
OriginalLicenseType = $_.LicenseType
ResourceGroup = $df.ResourceGroupName
Location = $df.Location
}
# Update the license type to $LicenseType.
if (-not $ReportOnly) {
$result = Set-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $df.ResourceGroupName -DataFactoryName $df.DataFactoryName -Name $_.Name -LicenseType $LicenseType -Force
$finalStatus += $result
Write-Host ([Environment]::NewLine + "-- DataFactory '$($df.DataFactoryName)' integration runtime updated to license type $LicenseType")
}
}
}
@@ -524,4 +723,9 @@ if ($modifiedResources.Count -gt 0) {
Write-Output "No resources were marked for modification. No CSV generated."
}
write-Output "Azure SQL Update Script completed"
Write-Output "Azure SQL Update Script completed"
$scriptEndTime = Get-Date
$executionDuration = $scriptEndTime - $scriptStartTime
Write-Output "Script execution ended at: $($scriptEndTime.ToString('yyyy-MM-dd HH:mm:ss'))"
Write-Output "Total execution time: $($executionDuration.ToString('hh\:mm\:ss'))"