Skip to content

Commit 34d0557

Browse files
authored
Cmdlet Update-AzModule (#12832)
* Update-AzModule * Address to PM requirements 1. Remove prefix Az. when input module name 2. update calculate from most recently installed respositories of different modules 3. reorder the sequence of module udpate and uninstall 4. other bug fix
1 parent cc9d652 commit 34d0557

File tree

7 files changed

+289
-69
lines changed

7 files changed

+289
-69
lines changed

tools/Az.Tools.Installer/Az.Tools.Installer.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
FunctionsToExport = '*'
7373

7474
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75-
CmdletsToExport = @('Install-AzModule', 'Uninstall-AzModule')
75+
CmdletsToExport = @('Install-AzModule', 'Uninstall-AzModule', 'Update-AzModule')
7676

7777
# Variables to export from this module
7878
# VariablesToExport = @()

tools/Az.Tools.Installer/Az.Tools.Installer.psm1

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,22 @@ elseif ($null -eq $module) {
2020
Import-Module PowerShellGet -MinimumVersion 2.1.3 -Scope Global
2121
}
2222

23-
$exportedPS1Files = Get-ChildItem -Path $PSScriptRoot/exports -Recurse -Include '*.ps1' -File
24-
$internalPS1Files = Get-ChildItem -Path $PSScriptRoot/internal -Recurse -Include '*.ps1' -File
25-
$exportedPS1Files | ForEach-Object { . $_.FullName }
26-
$internalPS1Files | ForEach-Object { . $_.FullName }
27-
Export-ModuleMember -Function $exportedPS1Files.Basename
23+
$exportedFunctions = @( Get-ChildItem -Path $PSScriptRoot/exports/*.ps1 -Recurse -ErrorAction SilentlyContinue )
24+
$internalFunctions = @( Get-ChildItem -Path $PSScriptRoot/internal/*.ps1 -ErrorAction SilentlyContinue )
25+
26+
$allFunctions = $exportedFunctions + $internalFunctions
27+
foreach($function in $allFunctions) {
28+
try {
29+
. $function.Fullname
30+
}
31+
catch {
32+
Write-Error -Message "Failed to import function $($function.fullname): $_"
33+
}
34+
}
35+
Export-ModuleMember -Function $exportedFunctions.Basename
36+
37+
$commandsWithRepositoryParameter = @(
38+
"Install-AzModule"
39+
)
40+
41+
Add-RepositoryArgumentCompleter -Cmdlets $commandsWithRepositoryParameter -ParameterName "Repository"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# ----------------------------------------------------------------------------------
2+
#
3+
# Copyright Microsoft Corporation
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
# ----------------------------------------------------------------------------------
14+
15+
function Get-AzModuleUpdateList {
16+
[OutputType([System.Collections.ArrayList])]
17+
[CmdletBinding(DefaultParameterSetName = 'Default', PositionalBinding = $false)]
18+
param(
19+
[Parameter(HelpMessage = 'The module name.')]
20+
[ValidateNotNullOrEmpty()]
21+
[string[]]
22+
${Name},
23+
24+
[Parameter(HelpMessage = 'The Registered Repostory.')]
25+
[ValidateNotNullOrEmpty()]
26+
[string]
27+
${Repository}
28+
)
29+
30+
process {
31+
32+
$modules = New-Object System.Collections.ArrayList
33+
34+
Write-Debug "Retrieving installed Az modules"
35+
$installModules = @{}
36+
try {
37+
(PowerShellGet\Get-InstalledModule -Name "Az*" -ErrorAction Stop).Where({
38+
($_.Author -eq 'Microsoft Corporation' -or $_.CompanyName -eq 'Microsoft Corporation') -and ($_.Name -match "Az(\.[a-zA-Z0-9]+)?$")
39+
}) | ForEach-Object {
40+
$installModules[$_.Name] = @()
41+
}
42+
}
43+
catch {
44+
Write-Warning $_
45+
}
46+
47+
Write-Debug "The Az modules currently installed: $($installModules.Keys)"
48+
49+
if ($installModules.Keys) {
50+
foreach ($key in $installModules.Keys.Clone()) {
51+
$installedModules = (PowerShellGet\Get-InstalledModule -Name $key -AllVersions).Where( { -not $_.AdditionalMetadata.IsPrerelease })
52+
foreach ($installed in $installedModules) {
53+
$installModules[$key] += [System.Tuple]::Create($installed.Version, $installed.Repository)
54+
}
55+
if($installModules[$key].Count -gt 1) {
56+
$installModules[$key] = ($installModules[$key] | Sort-Object -Property @{Expression={[Version]$_.Item1}} -Descending)
57+
}
58+
}
59+
}
60+
61+
$modulesToCheck = @()
62+
$modulesToCheck += if ($Name) {
63+
#Fixme:Damien
64+
$Name.Foreach({
65+
if ($installModules.ContainsKey($_) -and $installModules[$_]) {
66+
[System.Tuple]::Create($_, $installModules[$_][0].Item2)
67+
}
68+
})
69+
} else {
70+
$installModules.Keys.ForEach({[System.Tuple]::Create($_, $installModules[$_][0].Item2)})
71+
}
72+
73+
$moduleSet = [System.Collections.Generic.HashSet[string]]::new()
74+
$null = $modulesToCheck | ForEach-Object {$moduleSet.Add($_.Item1)}
75+
76+
$index = 0
77+
while($index -lt $modulesToCheck.Count) {
78+
$moduleName = $modulesToCheck[$index].Item1
79+
$repo = if ($Repository) {$Repository} else {$modulesToCheck[$index].Item2}
80+
if($repo) {
81+
$module = PowerShellGet\Find-Module -Name $moduleName -Repository $repo
82+
if ($module) {
83+
Write-Progress -Activity "Find Module" -CurrentOperation "$($module.Name) with the latest version $($module.Version)" -PercentComplete ($index / $modulesToCheck.Count * 100)
84+
$null = $modules.Add($module)
85+
foreach ($dep in $module.Dependencies.Name) {
86+
if (!$moduleSet.Contains($dep)) {
87+
$modulesToCheck += [System.Tuple]::Create($dep, $repo)
88+
$null = $moduleSet.Add($dep)
89+
}
90+
}
91+
}
92+
}
93+
else {
94+
Write-Warning "Failed to find Repository of $moduleName. The module cannot be updated."
95+
}
96+
$index += 1
97+
}
98+
Write-Progress -Activity "Find Module" -Completed
99+
$modules = ($modules | Sort-Object -Property Name -Unique)
100+
101+
Write-Debug "The modules to check for update: $($modules.Name)"
102+
103+
$modulesToUpdate = $modules.Where({ !$installModules.ContainsKey($_.Name) -or !$installModules[$_.Name] -or [Version]($_.Version) -gt [Version]$installModules[$_.Name][0].Item1 })
104+
if ("Az" -eq ($modulesToUpdate | Select-Object -First 1).Name) {
105+
$first, $rest = $modulesToUpdate
106+
$modulesToUpdate = (@() + $rest + $first)
107+
}
108+
109+
If (-not $modulesToUpdate) {
110+
Write-Warning "None of your specifed module needs upgrading."
111+
}
112+
else {
113+
Write-Debug "The modules contain update: $($modulesToUpdate.Name)"
114+
}
115+
Write-Output ($modulesToUpdate | ForEach-Object {
116+
New-Object -Type PSObject -Property @{
117+
'Name' = $_.Name
118+
'VersionToUpgrade' = $_.Version
119+
'InstalledVersion' = if ($installModules.ContainsKey($_.Name)) {$installModules[$_.Name].Item1} else {@()}
120+
'Repository' = $_.Repository
121+
}
122+
})
123+
}
124+
}

tools/Az.Tools.Installer/exports/Get-AzModuleWithUpdate.ps1

Lines changed: 0 additions & 63 deletions
This file was deleted.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# ----------------------------------------------------------------------------------
2+
#
3+
# Copyright Microsoft Corporation
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
# ----------------------------------------------------------------------------------
14+
15+
function Update-AzModule {
16+
[OutputType([PSCustomObject[]])]
17+
[CmdletBinding(DefaultParameterSetName = 'Default', PositionalBinding = $false, SupportsShouldProcess = $true)]
18+
param(
19+
[Parameter(HelpMessage = 'The module names.')]
20+
[ValidateNotNullOrEmpty()]
21+
[string[]]
22+
${Name},
23+
24+
[Parameter(HelpMessage = 'Present to decide whether to remove the previous versions of the modules.')]
25+
[switch]
26+
${RemovePrevious},
27+
28+
[Parameter(HelpMessage = 'Update modules and override warning messages about module installation conflicts. If a module with the same name already exists on the computer, Force allows for multiple versions to be installed. If there is an existing module with the same name and version, Force overwrites that version.')]
29+
[ValidateNotNullOrEmpty()]
30+
[Switch]
31+
${Force}
32+
)
33+
34+
process {
35+
$ppsedition = $PSVersionTable.PSEdition
36+
Write-Host "Powershell $ppsedition Version $($PSVersionTable.PSVersion)"
37+
38+
if ($ppsedition -eq "Core") {
39+
$allPahts = (Microsoft.PowerShell.Core\Get-Module -Name "Az*" -ListAvailable -ErrorAction Stop).Where({$_.Author -eq "Microsoft Corporation" -and $_.Name -match "Az(\.[a-zA-Z0-9]+)?$"}).Path
40+
$allPahts = ($allPahts | Select-String -Pattern "WindowsPowerShell")
41+
if ($allPahts) {
42+
Write-Warning "Az modules are also installed in WindowsPowerShell. Please update them using WindowsPowerShell."
43+
}
44+
}
45+
46+
$parameters = @{}
47+
if ($Name) {
48+
$Name = $Name.Foreach({"Az." + $_}) | Sort-Object -Unique
49+
$parameters['Name'] = $Name
50+
}
51+
52+
Write-Debug "Time Elapsed: $((Measure-Command { $allToUpdate = Get-AzModuleUpdateList @parameters }).TotalSeconds)s"
53+
54+
Write-Host -ForegroundColor DarkGreen "The modules to Upddate:$($allToUpdate | Out-String)"
55+
56+
if($allToUpdate.Count) {
57+
foreach ($module in $allToUpdate) {
58+
if ($Force -or $PSCmdlet.ShouldProcess($module.Name, "Upgrade to the latest version $($module.VersionToUpgrade) from $($module.Repository)")) {
59+
if ($Force) {
60+
Write-Debug "Upgrade $($module.Name) to the latest version $($module.VersionToUpgrade) from $($module.Repository)."
61+
}
62+
PowerShellGet\Update-Module -Name $module.Name -Force:$Force
63+
}
64+
if ($RemovePrevious) {
65+
if ($module.InstalledVersion -and ($Force -or $PSCmdlet.ShouldProcess($module.Name, "Uninstall the versions: $($module.InstalledVersion)"))) {
66+
if ($Force) {
67+
Write-Debug "Uninstall $($module.Name) of versions $($module.InstalledVersion)."
68+
}
69+
foreach($version in $module.InstalledVersion) {
70+
PowerShellGet\Uninstall-Module -Name $module.Name -RequiredVersion $version
71+
}
72+
}
73+
}
74+
}
75+
76+
$output = @()
77+
foreach($name in $allToUpdate.Name) {
78+
try {
79+
$output += (Get-InstalledModule -Name $name -ErrorAction Stop)
80+
}
81+
catch {
82+
Write-Warning $_
83+
}
84+
}
85+
Write-Output $output
86+
}
87+
}
88+
}
89+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
function Add-RepositoryArgumentCompleter()
2+
{
3+
[CmdletBinding()]
4+
param(
5+
[Parameter(Mandatory=$true)]
6+
[string[]]$cmdlets,
7+
8+
[Parameter(Mandatory=$true)]
9+
[string]$parameterName
10+
)
11+
12+
try
13+
{
14+
if(Get-Command -Name Register-ArgumentCompleter -ErrorAction SilentlyContinue)
15+
{
16+
Register-ArgumentCompleter -CommandName $cmdlets -ParameterName $parameterName -ScriptBlock {
17+
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
18+
19+
Get-PSRepository -Name "$wordTocomplete*"-ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Foreach-Object {
20+
[System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name)
21+
}
22+
}
23+
}
24+
}
25+
catch
26+
{
27+
# All this functionality is optional, so suppress errors
28+
Write-Debug -Message "Error registering argument completer: $_"
29+
}
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function Add-RepositoryDefaultValue()
2+
{
3+
[CmdletBinding()]
4+
param(
5+
[Parameter(Mandatory=$true)]
6+
[string[]]$cmdlets,
7+
8+
[Parameter(Mandatory=$true)]
9+
[string]$parameterName
10+
)
11+
12+
$defaultValueMap = Get-Variable PSDefaultParameterValues -Scope Global -ValueOnly
13+
try
14+
{
15+
foreach($cmdlet in $cmdlets) {
16+
$defaultValueMap.Add("${cmdlet}:$parameterName", {(Get-PSRepository).Where( {$_.SourceLocation.Contains('www.powershellgallery.com')}).Name})
17+
}
18+
}
19+
catch
20+
{
21+
# All this functionality is optional, so suppress errors
22+
Write-Debug -Message "Error registering argument completer: $_"
23+
}
24+
25+
Set-Variable -Scope Global -Name "PSDefaultParameterValues" -Value $defaultValueMap
26+
}

0 commit comments

Comments
 (0)