Functions/Public/New-AzUpgradeModulePlan.ps1
function New-AzUpgradeModulePlan { <# .SYNOPSIS Generates a new upgrade plan for migrating to the Az PowerShell module. .DESCRIPTION Generates a new upgrade plan for migrating to the Az PowerShell module. The upgrade plan details the specific file/offset points that require changes when moving from AzureRM commands to Az commands. .PARAMETER FromAzureRmVersion Specifies the AzureRM module version used in your existing PowerShell scripts(s) or modules. .PARAMETER ToAzVersion Specifies the Az module version to upgrade to. Currently, only Az version 4.4.0 is supported. .PARAMETER FilePath Specifies the path to a single PowerShell file. .PARAMETER DirectoryPath Specifies the path to a folder where PowerShell scripts or modules reside. .PARAMETER AzureRmCmdReference Specifies the AzureRM command references output from the Find-AzUpgradeCommandReference cmdlet. .EXAMPLE The following example generates a new Az module upgrade plan for the script file 'C:\Scripts\my-azure-script.ps1'. New-AzUpgradeModulePlan -FromAzureRmVersion 6.13.1 -ToAzVersion 4.4.0 -FilePath 'C:\Scripts\my-azure-script.ps1' .EXAMPLE The following example generates a new Az module upgrade plan for the script and module files located under C:\Scripts. New-AzUpgradeModulePlan -FromAzureRmVersion 6.13.1 -ToAzVersion 4.4.0 -DirectoryPath 'C:\Scripts' .EXAMPLE The following example generates a new Az module upgrade plan for the script and module files under C:\Scripts. Find-AzUpgradeCommandReference -DirectoryPath 'C:\Scripts' -AzureRmVersion '6.13.1' | New-AzUpgradeModulePlan -ToAzVersion 4.4.0 #> [CmdletBinding()] Param ( [Parameter( Mandatory=$true, ParameterSetName="FromReferences", ValueFromPipeline=$true, HelpMessage='Specify the AzureRM command references collection output from the Find-AzUpgradeCommandReference cmdlet.')] [CommandReferenceCollection] $AzureRmCmdReference, [Parameter( Mandatory=$true, ParameterSetName="FromNewSearchByFile", HelpMessage='Specify the Az module version to upgrade to.')] [Parameter( Mandatory=$true, ParameterSetName="FromNewSearchByDirectory", HelpMessage='Specify the Az module version to upgrade to.')] [System.String] [ValidateSet('6.13.1')] $FromAzureRmVersion, [Parameter( Mandatory=$true, ParameterSetName="FromNewSearchByFile", HelpMessage="Specify the path to a single PowerShell file.")] [System.String] [ValidateNotNullOrEmpty()] $FilePath, [Parameter( Mandatory=$true, ParameterSetName="FromNewSearchByDirectory", HelpMessage="Specify the path to the folder where PowerShell scripts or modules reside.")] [System.String] [ValidateNotNullOrEmpty()] $DirectoryPath, [Parameter( Mandatory=$true, HelpMessage='Specify the Az module version to upgrade to.')] [System.String] [ValidateSet('4.4.0')] $ToAzVersion ) Process { $cmdStarted = Get-Date # if an existing set of command references was not provided # then call the Find cmdlet to search for those references. if ($PSCmdlet.ParameterSetName -eq 'FromNewSearchByFile') { Write-Verbose -Message "Searching for commands to upgrade, by file." $AzureRmCmdReference = Find-AzUpgradeCommandReference -FilePath $FilePath -AzureRmVersion $FromAzureRmVersion } elseif ($PSCmdlet.ParameterSetName -eq 'FromNewSearchByDirectory') { Write-Verbose -Message "Searching for commands to upgrade, by directory." $AzureRmCmdReference = Find-AzUpgradeCommandReference -DirectoryPath $DirectoryPath -AzureRmVersion $FromAzureRmVersion } # we can't generate an upgrade plan without some cmdlet references, so quit early here if required. if ($AzureRmCmdReference -eq $null -or $AzureRmCmdReference.Items.Count -eq 0) { Write-Verbose -Message "No AzureRm command references were found. No upgrade plan will be generated." return } else { Write-Verbose -Message "$($AzureRmCmdReference.Items.Count) AzureRm command reference(s) were found. Upgrade plan will be generated." } Write-Verbose -Message "Importing cmdlet spec for Az $ToAzVersion" $azCmdlets = Import-CmdletSpec -ModuleName "Az" -ModuleVersion $ToAzVersion Write-Verbose -Message "Importing upgrade alias spec for Az $ToAzVersion" $upgradeAliases = Import-AliasSpec -ModuleVersion $ToAzVersion $defaultParamNames = @("Debug", "ErrorAction", "ErrorVariable", "InformationAction", "InformationVariable", "OutVariable", "OutBuffer", "PipelineVariable", "Verbose", "WarningAction", "WarningVariable", "WhatIf", "Confirm") # synchronous results output instead of async. the reason for this is that # downstream commands will need the entire results object to process at once. $upgradePlan = New-Object -TypeName UpgradePlan foreach ($rmCmdlet in $AzureRmCmdReference.Items) { Write-Verbose -Message "Checking upgrade potential for instance of $($rmCmdlet.CommandName)" if ($rmCmdlet.HasSplattedArguments -eq $true) { $warningMsg = New-Object -TypeName UpgradePlanResult $warningMsg.Command = $rmCmdlet $warningMsg.Reason = "Cmdlet invocation uses splatted parameters. Consider unrolling to allow automated parameter upgrade checks." $warningMsg.ReasonCode = [UpgradePlanResultReasonCode]::WarningSplattedParameters $upgradePlan.Warnings.Add($warningMsg) } if ($upgradeAliases.ContainsKey($rmCmdlet.CommandName) -eq $false) { $errorMsg = New-Object -TypeName UpgradePlanResult $errorMsg.Command = $rmCmdlet $errorMsg.Reason = "No matching upgrade alias found. Command cannot be automatically upgraded." $errorMsg.ReasonCode = [UpgradePlanResultReasonCode]::ErrorNoUpgradeAlias $upgradePlan.Errors.Add($errorMsg) continue } $resolvedCommandName = $upgradeAliases[$rmCmdlet.CommandName] if ($azCmdlets.ContainsKey($resolvedCommandName) -eq $false) { $errorMsg = New-Object -TypeName UpgradePlanResult $errorMsg.Command = $rmCmdlet $errorMsg.Reason = "No Az cmdlet spec found for $resolvedCommandName. Command cannot be automatically upgraded." $errorMsg.ReasonCode = [UpgradePlanResultReasonCode]::ErrorNoModuleSpecMatch $upgradePlan.Errors.Add($errorMsg) continue } $cmdletUpgrade = New-Object -TypeName CmdletUpgradeStep $cmdletUpgrade.OriginalCmdletName = $rmCmdlet.CommandName $cmdletUpgrade.ReplacementCmdletName = $resolvedCommandName $cmdletUpgrade.FullPath = $rmCmdlet.FullPath $cmdletUpgrade.FileName = $rmCmdlet.FileName $cmdletUpgrade.StartLine = $rmCmdlet.StartLine $cmdletUpgrade.StartColumn = $rmCmdlet.StartColumn $cmdletUpgrade.EndLine = $rmCmdlet.EndLine $cmdletUpgrade.EndPosition = $rmCmdlet.EndPosition $cmdletUpgrade.StartOffset = $rmCmdlet.StartOffset $cmdletUpgrade.EndOffset = $rmCmdlet.EndOffset $upgradePlan.UpgradeSteps.Add($cmdletUpgrade) # check if parameters need to be updated if ($rmCmdlet.Parameters.Count -gt 0) { $resolvedAzCommand = $azCmdlets[$resolvedCommandName] foreach ($rmParam in $rmCmdlet.Parameters) { if ($defaultParamNames -contains $rmParam.Name) { # direct match to a built-in default parameter # no changes required. continue } $matchedDirectName = $resolvedAzCommand.Parameters | Where-Object -FilterScript { $_.Name -eq $rmParam.Name } if ($matchedDirectName -ne $null) { # direct match to the upgraded cmdlet's parameter name. # no changes required. continue } $matchedAliasName = $resolvedAzCommand.Parameters | Where-Object -FilterScript { $_.Aliases -contains $rmParam.Name } if ($matchedAliasName -ne $null) { # alias match to the upgraded cmdlet's parameter name. # we should add an upgrade step to swap to use the non-aliased name. $paramUpgrade = New-Object -TypeName CmdletParameterUpgradeStep $paramUpgrade.OriginalParameterName = $rmParam.Name $paramUpgrade.ReplacementParameterName = $matchedAliasName.Name # properties from the parent cmdlet $paramUpgrade.FullPath = $rmCmdlet.FullPath $paramUpgrade.FileName = $rmCmdlet.FileName # properties from the parameter itself $paramUpgrade.StartLine = $rmParam.StartLine $paramUpgrade.StartColumn = $rmParam.StartColumn $paramUpgrade.EndLine = $rmParam.EndLine $paramUpgrade.EndPosition = $rmParam.EndPosition $paramUpgrade.StartOffset = $rmParam.StartOffset $paramUpgrade.EndOffset = $rmParam.EndOffset $upgradePlan.UpgradeSteps.Add($paramUpgrade) continue } # no direct match and no alias match? # this could mean a breaking change that requires manual adjustments $errorMsg = New-Object -TypeName UpgradePlanResult $errorMsg.Command = $rmCmdlet $errorMsg.Reason = "Parameter [$($rmParam.Name)] was not found in $resolvedCommandName or it's aliases." $errorMsg.ReasonCode = [UpgradePlanResultReasonCode]::ErrorParameterNotFound $upgradePlan.Errors.Add($errorMsg) } } } # sort the upgrade steps to by file, then offset descending. # the reason for this is updates must be made in descending offset order # otherwise file positions will change for subsequent swaps. $filter1 = @{ Expression = 'FullPath'; Ascending = $true } $filter2 = @{ Expression = 'StartOffset'; Descending = $true } $upgradePlan.UpgradeSteps = $upgradePlan.UpgradeSteps | Sort-Object -Property $filter1, $filter2 # now that we have a sorted collection, add in the step order number # for extra clarity in the upgrade plan. for ([int]$i = 0; $i -lt $upgradePlan.UpgradeSteps.Count; $i++) { $upgradePlan.UpgradeSteps[$i].StepNumber = ($i + 1) } Send-MetricsIfDataCollectionEnabled -Operation Plan ` -ParameterSetName $PSCmdlet.ParameterSetName ` -Duration ((Get-Date) - $cmdStarted) ` -Properties ([PSCustomObject]@{ ToAzureModuleName = "Az" ToAzureModuleVersion = $ToAzVersion UpgradeStepsCount = $upgradePlan.UpgradeSteps.Count PlanWarnings = $upgradePlan.Warnings PlanErrors = $upgradePlan.Errors }) Write-Output -InputObject $upgradePlan } } |