diff --git a/samples/manage/.vscode/launch.json b/samples/manage/.vscode/launch.json new file mode 100644 index 00000000..699f527d --- /dev/null +++ b/samples/manage/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "PowerShell: Launch Current File", + "type": "PowerShell", + "request": "launch", + "script": "${file}", + "args": [] + } + ] +} \ No newline at end of file diff --git a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-arc-sql-license-type.ps1 b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-arc-sql-license-type.ps1 index 81a5e698..ac6e8bc6 100644 --- a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-arc-sql-license-type.ps1 +++ b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-arc-sql-license-type.ps1 @@ -140,7 +140,7 @@ if($null -ne $ExclusionTags){ } if (-not $TenantId) { - $TenantId = (Get-AzContext).Tenant.Id + $TenantId = (Get-AzContext).Tenant.Id Write-Output "No TenantId provided. Using current context TenantId: $TenantId" } else { Write-Output "Using provided TenantId: $TenantId" diff --git a/samples/manage/azure-hybrid-benefit/modify-license-type/modify-azure-sql-license-type.ps1 b/samples/manage/azure-hybrid-benefit/modify-license-type/modify-azure-sql-license-type.ps1 index 20f815e9..192c2f43 100644 --- a/samples/manage/azure-hybrid-benefit/modify-license-type/modify-azure-sql-license-type.ps1 +++ b/samples/manage/azure-hybrid-benefit/modify-license-type/modify-azure-sql-license-type.ps1 @@ -12,7 +12,6 @@ CSV List of Subscriptions: Process multiple subscriptions provided in a CSV file. All Accessible Subscriptions: Automatically detect and update all subscriptions that you have access to. -For specific resource types like SQL Virtual Machines and SQL Managed Instances, the script can optionally start the resource if it is in a stopped state (when the -Force_Start_On_Resources parameter is enabled) before applying the license update. The script processes several types of Azure SQL resources including: @@ -36,9 +35,6 @@ This automation helps ensure that your licensing configuration is consistent acr .PARAMETER LicenseType Optional. License type to set. Allowed values: "LicenseIncluded" (default) or "BasePrice". -.PARAMETER Force_Start_On_Resources - Optional. If true, starts SQL VMs and SQL Managed Instances before updating their license type. - .PARAMETER ExclusionTags Optional. If specified, excludes the resources that have this tag assigned. @@ -55,7 +51,7 @@ This automation helps ensure that your licensing configuration is consistent acr param ( [Parameter(Mandatory = $false)] - [string] $SubId=$null, + [string] $SubId="$null", [Parameter(Mandatory = $false)] [string] $ResourceGroup=$null, @@ -64,9 +60,6 @@ param ( [ValidateSet("LicenseIncluded", "BasePrice", IgnoreCase = $false)] [string] $LicenseType = "LicenseIncluded", - [Parameter(Mandatory = $false)] - [switch] $Force_Start_On_Resources, - [Parameter (Mandatory= $false)] [object] $ExclusionTags, @@ -150,7 +143,7 @@ if($null -ne $ExclusionTags){ } if (-not $TenantId) { - $TenantId = $context.Tenant.Id + $TenantId = (Get-AzContext).Tenant.Id Write-Output "No TenantId provided. Using current context TenantId: $TenantId" } else { Write-Output "Using provided TenantId: $TenantId" @@ -162,6 +155,52 @@ if ($UseManagedIdentity) { 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." + } +} + +# Import Az.Accounts with minimum version requirement +try { + Import-Module Az.Accounts -MinimumVersion 4.2.0 -Force + Write-Host "Az.Accounts module imported successfully." +} catch { + Write-Error "Failed to import Az.Accounts: $_" + return +} + +# Ensure Az.DataFactory is available and import it +try { + if (-not (Get-Module -ListAvailable -Name Az.DataFactory)) { + Write-Host "Az.DataFactory module not found. Installing..." + Install-Module -Name Az.DataFactory -Scope CurrentUser -Force + } else { + Write-Host "Az.DataFactory module is already installed." + } + Import-Module Az.DataFactory -Force +} catch { + Write-Error "Can't import module Az.DataFactory: $_" +} + # Map License Types for SQL VMs: LicenseIncluded -> PAYG, BasePrice -> AHUB. $SqlVmLicenseType = if ($LicenseType -eq "LicenseIncluded") { "PAYG" } else { "AHUB" } @@ -218,9 +257,9 @@ foreach ($sub in $subscriptions) { try { Write-Output "Seeking SQL Virtual Machines that require a license update to $SqlVmLicenseType..." $sqlVmQuery = if ($rgFilter) { - "[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' && $rgFilter $tagsFilter]" + "[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' && $rgFilter $tagsFilter].{name:name, resourceGroup:resourceGroup, sqlServerLicenseType:sqlServerLicenseType, type:type, id:id, Location:location}" } else { - "[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' $tagsFilter]" + "[?sqlServerLicenseType!='${SqlVmLicenseType}' && sqlServerLicenseType!= 'DR' $tagsFilter].{name:name, resourceGroup:resourceGroup, sqlServerLicenseType:sqlServerLicenseType, type:type, id:id, Location:location}" } @@ -242,11 +281,11 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $sqlvm.SubscriptionId + SubID = ($sqlvm.id -split '/')[2] ResourceName = $sqlvm.name - ResourceType = $sqlvm.ResourceType - Status = $sqlvm.Status - OriginalLicenseType = $sqlvm.licenseType + ResourceType = "Microsoft.SqlVirtualMachine/sqlVirtualMachines" + Status = $vmStatus.PowerState + OriginalLicenseType = $sqlvm.sqlServerLicenseType ResourceGroup = $sqlvm.resourceGroup Location = $sqlvm.Location # Cores @@ -259,13 +298,6 @@ foreach ($sub in $subscriptions) { $finalStatus += $result } } - else { - if ($Force_Start_On_Resources -and -not $ReportOnly) { - Write-Output "SQL VM '$($sqlvm.name)' is not running. Forcing start to update license..." - az vm start --resource-group $sqlvm.resourceGroup --name $sqlvm.name --no-wait yes - $sqlVmsToUpdate.Add($sqlvm) | Out-Null - } - } } else { Write-Output "SQL VM '$($sqlvm.name)' in RG '$($sqlvm.resourceGroup)' Skipping because of tags ($tags)..." @@ -284,35 +316,20 @@ foreach ($sub in $subscriptions) { # --- Section: Update SQL Managed Instances (Stopped then Ready) "hybridSecondaryUsage": "Passive"--- $sqlMIsToUpdate = [System.Collections.ArrayList]::new() try { - if ($Force_Start_On_Resources) { - Write-Output "Seeking SQL Managed Instances that are stopped and require an update to $LicenseType..." - $miQuery = if ($rgFilter) { - "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state!='Ready' && $rgFilter $tagsFilter].{Name:name, State:state, ResourceGroup:resourceGroup}" - } else { - "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state!='Ready' $tagsFilter].{Name:name, State:state, ResourceGroup:resourceGroup}" - } - Write-Output "Seeking SQL Managed Instances with Filter $miQuery..." - $offSQLMIs = az sql mi list --query $miQuery -o json | ConvertFrom-Json - if( $offSQLMIs.Count -eq 0) { - Write-Output "No SQL Managed Instances found to Start that require a license update." - } else { - Write-Output "Found $($offSQLMIs.Count) SQL Managed Instances found to Start that require a license update." - } - foreach ($mi in $offSQLMIs) { - if ($mi.State -eq "Stopped" -and -not $ReportOnly) { - Write-Output "Starting SQL Managed Instance '$($mi.Name)' in RG '$($mi.ResourceGroup)'..." - az sql mi start --mi $mi.Name -g $mi.ResourceGroup --no-wait yes - } - $sqlMIsToUpdate.Add($mi) | Out-Null - } + + Write-Output "Processing SQL Managed Instances that are running to $LicenseType..." + $miRunningQuery = "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state=='Ready'" + + if ($rgFilter) { + $miRunningQuery += " && $rgFilter" } - Write-Output "Processing SQL Managed Instances that are running to $LicenseType..." - $miRunningQuery = if ($rgFilter) { - "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state=='Ready' && $rgFilter $tagsFilter]" - } else { - "[?licenseType!='${LicenseType}' && hybridSecondaryUsage!='Passive' && state=='Ready' $tagsFilter]" + if ($tagsFilter) { + $miRunningQuery += " $tagsFilter" } + + $miRunningQuery += "].{name:name, state:state, resourceGroup:resourceGroup, licenseType:licenseType, location:location, id:id, ResourceType:type}" + Write-Output "Processing SQL Managed Instances that are running with filter $miRunningQuery..." $runningMIs = az sql mi list --query $miRunningQuery -o json | ConvertFrom-Json if($runningMIs.Count -eq 0) { @@ -325,7 +342,7 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $mi.SubscriptionId + SubID = ($mi.id -split '/')[2] ResourceName = $mi.name ResourceType = $mi.ResourceType Status = $mi.State @@ -354,7 +371,7 @@ foreach ($sub in $subscriptions) { 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]" -o json | ConvertFrom-Json + $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 { @@ -365,7 +382,7 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $db.SubscriptionId + SubID = ($db.id -split '/')[2] ResourceName = $db.name ResourceType = $db.ResourceType Status = $db.State @@ -384,7 +401,7 @@ foreach ($sub in $subscriptions) { # 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]" --only-show-errors -o json | ConvertFrom-Json + $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." } else { @@ -395,7 +412,7 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $pool.SubscriptionId + SubID = ($pool.id -split '/')[2] ResourceName = $pool.name ResourceType = $pool.ResoureType Status = $pool.State @@ -424,7 +441,11 @@ foreach ($sub in $subscriptions) { # --- 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]" } else { "[?licenseType!='${LicenseType}' $tagsFilter]" } + $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}" + } $instancePools = az sql instance-pool list --query $instancePoolsQuery -o json | ConvertFrom-Json $poolsToUpdate = $instancePools | Where-Object { $_.licenseType -ne $LicenseType } if($poolsToUpdate.Count -eq 0) { @@ -437,7 +458,7 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $pool.SubscriptionId + SubID = ($pool.id -split '/')[2] ResourceName = $pool.name ResourceType = $pool.ResoureType Status = $pool.State @@ -465,9 +486,9 @@ foreach ($sub in $subscriptions) { # Collect data before modification $modifiedResources += [PSCustomObject]@{ TenantID = $TenantId - SubID = $_.SubscriptionId + SubID = ($_.Id -split '/')[2] ResourceName = $_.Name - ResourceType = $_.ResoureType + ResourceType = "Microsoft.DataFactory/factories/integrationRuntimes" Status = $_.State OriginalLicenseType = $_.LicenseType ResourceGroup = $_.ResourceGroupName @@ -487,97 +508,6 @@ foreach ($sub in $subscriptions) { Write-Error "An error occurred while updating DataFactory SSIS Integration Runtimes: $_" } - # --- Section: Finalize SQL VM updates for those that were started on-demand --- - $sqlvm = "" - try { - $updated = $true - while ($sqlVmsToUpdate.Count -gt 0) { - $sqlvm = $sqlVmsToUpdate[0] - $vmStatus = az vm get-instance-view --resource-group $sqlvm.resourceGroup --name $sqlvm.name --query "{PowerState:instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus | [0]}" -o json | ConvertFrom-Json - if ($vmStatus.PowerState -eq "VM running") { - # Collect data before modification - $modifiedResources += [PSCustomObject]@{ - TenantID = $TenantId - SubID = $sqlvm.SubscriptionId - ResourceName = $sqlvm.name - ResourceType = $sqlvm.ResourceType - Status = $sqlvm.State - OriginalLicenseType = $sqlvm.licenseType - ResourceGroup = $sqlvm.resourceGroup - Location = $sqlvm.Location - # Cores - } - if (-not $ReportOnly) { - Write-Output "Now updating SQL VM '$($sqlvm.name)' after forced start..." - $result = az sql vm update -n $sqlvm.name -g $sqlvm.resourceGroup --license-type $SqlVmLicenseType -o json | ConvertFrom-Json - $finalStatus += $result - Write-Output "Deallocating SQL VM '$($sqlvm.name)' post-update..." - az vm deallocate --resource-group $sqlvm.resourceGroup --name $sqlvm.name --no-wait yes - $sqlVmsToUpdate.RemoveAt(0) - $updated = $true - } - } - else { - if ($updated) { - Write-Host "Waiting for SQL VM '$($sqlvm.name)' to start..." - $updated = $false - } - else { - Write-Host "." -NoNewline - } - Start-Sleep -Seconds 30 - } - } - } - catch { - Write-Error "An error occurred while finalizing SQL VM updates in subscription '$($sub.name)': $sqlvm" - } - - # --- Section: Finalize SQL Managed Instance updates for those that were forced-start --- - $mi = "" - try { - $updated = $true - while ($sqlMIsToUpdate.Count -gt 0) { - $mi = $sqlMIsToUpdate[0] - $miStatus = az sql mi show --resource-group $mi.ResourceGroup --name $mi.Name -o json | ConvertFrom-Json - if ($miStatus.state -eq "Ready") { - # Collect data before modification - $modifiedResources += [PSCustomObject]@{ - TenantID = $TenantId - SubID = $mi.SubscriptionId - ResourceName = $mi.name - ResourceType = $mi.ResourceType - Status = $mi.State - OriginalLicenseType = $mi.licenseType - ResourceGroup = $mi.resourceGroup - Location = $mi.location - } - if (-not $ReportOnly) { - Write-Output "Updating SQL Managed Instance '$($mi.Name)' after forced start..." - $result = az sql mi update --name $mi.Name --resource-group $mi.ResourceGroup --license-type $LicenseType -o json | ConvertFrom-Json - $finalStatus += $result - Write-Output "Stopping SQL Managed Instance '$($mi.Name)' post-update..." - az sql mi stop --resource-group $mi.ResourceGroup --mi $mi.Name --no-wait yes - $sqlMIsToUpdate.RemoveAt(0) - $updated = $true - } - } - else { - if ($updated) { - Write-Host "Waiting for SQL Managed Instance '$($mi.Name)' to be ready..." - $updated = $false - } - else { - Write-Host "." -NoNewline - } - Start-Sleep -Seconds 30 - } - } - } - catch { - Write-Error "An error occurred while finalizing SQL Managed Instance updates in subscription '$($sub.name)': $mi" - } - } catch { Write-Error "An error occurred while processing subscription '$($sub.name)': $_" @@ -602,4 +532,4 @@ if ($modifiedResources.Count -gt 0) { Write-Output "No resources were marked for modification. No CSV generated." } -write-Output "Azure SQL Update Script completed" \ No newline at end of file +write-Output "Azure SQL Update Script completed" \ No newline at end of file