Public/Authentication/Update-JCModule.ps1
Function Update-JCModule { [CmdletBinding()] [OutputType([System.Boolean])] Param( [Parameter(HelpMessage = 'Skips the "Uninstall-Module" step that will uninstall old version of the module.')][Switch]$SkipUninstallOld , [Parameter(HelpMessage = 'ByPasses user prompts.')][Switch]$Force , [Parameter(DontShow, ParameterSetName = 'CodeArtifact', HelpMessage = 'Specify the credentials for repository to pull from.')][System.Management.Automation.PSCredential]$RepositoryCredentials , [Parameter(DontShow, ParameterSetName = 'CodeArtifact', HelpMessage = 'Switch to toggle CodeArtifact Updates')][Switch]$CodeArtifact ) Begin { # Update Status $updateStatus = $false # JumpCloud Module Name $ModuleName = 'JumpCloud' # Module Names for the SDKs $SDKs = @('JumpCloud.SDK.DirectoryInsights' 'JumpCloud.SDK.V2' 'JumpCloud.SDK.V1') # Set Repository $Repository = if ($CodeArtifact) { 'CodeArtifact' } else { 'PSGallery' } # Module Root Path $ModuleRoot = (Get-Item -Path:($PSScriptRoot)).Parent.Parent.FullName # Get Modules & SDKs From Remote & Locally Installed if ($CodeArtifact) { $FoundModule = Get-JCLatestModule -ModuleName:($ModuleName) -Repository:($Repository) -RepositoryCredentials:($RepositoryCredentials) -CodeArtifact $FoundSDKs = foreach ($SDK in $SDKs) { Get-JCLatestModule -ModuleName:($SDK) -Repository:($Repository) -RepositoryCredentials:($RepositoryCredentials) -CodeArtifact } $InstalledModulePreUpdate = Get-JCInstalledModule -ModuleName:($ModuleName) -CodeArtifact $InstalledSDKsPreUpdate = foreach ($SDK in $SDKs) { Get-JCInstalledModule -ModuleName:($SDK) -CodeArtifact } } else { $FoundModule = Get-JCLatestModule -ModuleName:($ModuleName) -Repository:($Repository) $FoundSDKs = foreach ($SDK in $SDKs) { Get-JCLatestModule -ModuleName:($SDK) -Repository:($Repository) } $InstalledModulePreUpdate = Get-JCInstalledModule -ModuleName:($ModuleName) $InstalledSDKsPreUpdate = foreach ($SDK in $SDKs) { Get-JCInstalledModule -ModuleName:($SDK) } } # SDK Lists $SDKsToUninstall = @() $SDKsToUpdate = @() $SDKsUpToDate = @() $SDKsInstalledSummary = @() $SDKUpdateTable = @() $SDKResultsSummary = @() $SDKUninstallSummary = @() # Build welcome page $WelcomePage = New-Object -TypeName:('PSCustomObject') } Process { # Load color scheme $JCColorConfig = Get-JCColorConfig # Update SDKs # Gather info about the installed SDKs/ What needs to be updated/ removed # For each installed SDK, check if it's out of date foreach ($installedSDK in $InstalledSDKsPreUpdate) { $latest = $FoundSDKs | Where-Object { $_.Name -eq $($installedSDK.Name) } If ($installedSDK.Version -notin $latest.Version) { $SDKUpdateTable += [PSCustomObject]@{ 'SDK Name' = $($installedSDK.Name) 'Installed Version' = $($installedSDK.Version) 'Latest Version' = $($latest.Version) 'Update Action' = 'Update' } $SDKsToUpdate += $installedSDK } else { $SDKUpdateTable += [PSCustomObject]@{ 'SDK Name' = $($installedSDK.Name) 'Installed Version' = $($installedSDK.Version) 'Latest Version' = $($latest.Version) 'Update Action' = 'No Action' } $SDKsUpToDate += $installedSDK } } # Compare InstalledSDKs to the Up To Date List $ComparedSDKs = Compare-Object -ReferenceObject $InstalledSDKsPreUpdate -DifferenceObject $SDKsUpToDate -Property Version, Name -IncludeEqual # If multiple versions of an installed SDK exist, add to uninstall list - Here we deal with multiple versions $groupCompare = $ComparedSDKs | Group-Object Name foreach ($item in $groupCompare) { # Latest Installed, Older Installed as well if ('==' -in $item.group.sideIndicator) { $SDKsToUninstall += $item.Group | Where-Object { $_.SideIndicator -ne '==' } } elseif ($item.Count -gt 1 -and '==' -notin $item.group.sideIndicator) { $SDKsToUninstall += $item.Group | Sort-Object -Property Version -Descending | Select-Object -Skip 1 } } # Compare InstalledSDKs to the Up To Date List, what remains should be our update list $ComparedSDKsToUpdate = Compare-Object -ReferenceObject $SDKsToUninstall -DifferenceObject $SDKsToUpdate -Property Version, Name # Update our update table to show the user: foreach ($updateItem in $SDKUpdateTable) { foreach ($uninstallItem in $SDKsToUninstall) { if (($updateItem.'SDK Name' -eq $uninstallItem.Name) -And ($($updateItem.'Installed Version')) -eq $uninstallItem.Version) { If (!($SkipUninstallOld)) { $updateItem.'Update Action' = 'Uninstall' } else { $updateItem.'Update Action' = 'No Action' } } } } # If there are changes to the SDKs which should be made, prompt if (("Update" -in $SDKUpdateTable.'Update Action')) { # Populate status message $WelcomePage = $WelcomePage | Select-Object @{Name = 'STATUS'; Expression = { 'An update is available for the JumpCloud SDK module(s) PowerShell module.' } }, * $WelcomePage.PSObject.Properties.Name | ForEach-Object { If (-not [System.String]::IsNullOrEmpty($WelcomePage.($_))) { Write-Host (($_) + ': ') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Header) $WelcomePage.($_).Trim() -split ("`n") | ForEach-Object { If (-not [System.String]::IsNullOrEmpty(($_))) { Write-Host ($JCColorConfig.IndentChar) -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Indentation) -NoNewline Write-Host (($_).Trim())-BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Body) } } } } # Write-Host "An update is avaiable for the installed JumpCloud SDK module(s)" if ($PSBoundParameters.Debug -eq $true) { $SDKUpdateTable | Format-Table | Out-Host } else { # Print streamlined version $SDKUpdateTable | Where-Object { ($_.'Update Action' -ne 'uninstall') -And ($_.'Update Action' -ne 'No Action') } | Select-Object 'SDK Name', 'Installed Version', 'Latest Version' | Format-Table | Out-Host } # Ask user if they want to update the module If (!($Force)) { Do { Write-Host ('Enter ''Y'' to update the ' + $ModuleName + ' SDK modules to the latest version or enter ''N'' to cancel:') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_UserPrompt) -NoNewline Write-Host (' ') -NoNewline $UserInput = Read-Host } Until ($UserInput.ToUpper() -in ('Y', 'N')) } Else { $UserInput = 'Y' } If ($UserInput.ToUpper() -eq 'N') { Write-Host ('Skipping the ' + $ModuleName + ' SDK module update process.') } Else { # For each SDK in update list where we need to update: foreach ($SDK in $SDKUpdateTable | Where-Object { $_."Update Action" -eq 'Update' }) { try { Write-Debug -Message "Running Command: Install-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Latest Version") -Force" Install-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Latest Version") -Force $SDKsInstalledSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Target Version' = $($SDK."Latest Version") 'Install Success' = $true } } catch { $SDKsInstalledSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Target Version' = $($SDK."Latest Version") 'Install Success' = $false } Write-Warning -Message "$($SDK."SDK Name") Could not be updated automatically; run the following command to install manually:" Write-Warning -Message "Install-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Latest Version") -Force" } # Get and remove the current module Get-Module -Name:($SDK."SDK Name") -ListAvailable -All | Remove-Module -Force try { Write-Debug -Message "Running Command: Import-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Latest Version") -Force" Import-Module $SDK."SDK Name" -RequiredVersion $($SDK."Latest Version") -Scope:('Global') -Force $SDKResultsSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Imported' = $true } } catch { Write-Debug -Message "Error: Import-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Latest Version") -Force" $SDKResultsSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Imported' = $false } } If (Get-InstalledModule -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version")) { Try { Write-Debug -Message "Running Command: Uninstall-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version") -Force" Uninstall-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version") -Force $SDKUninstallSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Uninstalled Version' = $($SDK."Installed Version") 'Uninstalled' = $true } } Catch { Write-Warning -Message "Could not uninstall $($SDK."SDK Name") $($SDK."Installed Version")" $SDKUninstallSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Uninstalled Version' = $($SDK."Installed Version") 'Uninstalled' = $false } } } } # For each SDK in uninstall list If (!($SkipUninstallOld)) { if ($PSBoundParameters.Debug -eq $true -And $SDKsToUninstall.Count -ge 1) { Write-Debug "The following out-of-date SDK Module(s) will be uninstalled" $SDKUpdateTable | Where-Object { $_.'Update Action' -eq 'uninstall' } | Format-Table | Out-Host } foreach ($SDK in $SDKUpdateTable | Where-Object { $_."Update Action" -eq 'Uninstall' }) { If (Get-InstalledModule -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version")) { Try { Write-Debug -Message "Running Command: Uninstall-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version") -Force" Uninstall-Module -Name $($SDK."SDK Name") -RequiredVersion $($SDK."Installed Version") -Force $SDKUninstallSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Uninstalled Version' = $($SDK."Installed Version") 'Uninstalled' = $true } } Catch { Write-Warning -Message "Could not uninstall $($SDK."SDK Name") $($SDK."Installed Version")" $SDKUninstallSummary += [PSCustomObject]@{ 'SDK Name' = $($SDK."SDK Name") 'Uninstalled Version' = $($SDK."Installed Version") 'Uninstalled' = $false } } } } } } } # Check to see if module is already installed If ([System.String]::IsNullOrEmpty($InstalledModulePreUpdate)) { Write-Error ('The ' + $ModuleName + ' PowerShell module is not currently installed. To install the module please run the following command: Install-Module -Name ' + $ModuleName + ' -force;' ) } Else { # Populate status message $Status = If ($FoundModule.Version -notin $InstalledModulePreUpdate.Version) { 'An update is available for the ' + $ModuleName + ' PowerShell module.' } ElseIf ($FoundModule.Version -in $InstalledModulePreUpdate.Version) { 'The ' + $ModuleName + ' PowerShell module is up to date.' } Else { Write-Error ('Unable to determine ' + $ModuleName + ' PowerShell module install status.') } # Build the welcomePage Message $WelcomePage = New-Object -TypeName:('PSCustomObject') | Select-Object ` @{Name = 'INSTALLED VERSION(S)'; Expression = { $InstalledModulePreUpdate | ForEach-Object { ($_.Version).ToString() + ' (' + (Get-Date $_.PublishedDate).ToString('MMMM dd, yyyy') + ')' } } } If ($FoundModule.Version -notin $InstalledModulePreUpdate.Version) { # If there is an update, display the latest version $versionString = $FoundModule.Version.ToString() + ' (' + (Get-Date $FoundModule.PublishedDate).ToString('MMMM dd, yyyy') + ')' $WelcomePage | Add-Member -MemberType NoteProperty -Name "LATEST VERSION" -Value $versionString } $WelcomePage | Add-Member -MemberType NoteProperty -Name "Learn more about the $ModuleName PowerShell module here" -Value "https://github.com/TheJumpCloud/support/wiki" $WelcomePage = $WelcomePage | Select-Object @{Name = 'STATUS'; Expression = { $Status } }, * # Display message $WelcomePage.PSObject.Properties.Name | ForEach-Object { If (-not [System.String]::IsNullOrEmpty($WelcomePage.($_))) { Write-Host (($_) + ': ') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Header) $WelcomePage.($_).Trim() -split ("`n") | ForEach-Object { If (-not [System.String]::IsNullOrEmpty(($_))) { Write-Host ($JCColorConfig.IndentChar) -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Indentation) -NoNewline If (($_) -like '*http*' -or ($_) -like '*www.*' -or ($_) -like '*.com*') { Write-Host (($_).Trim())-BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Url) } ElseIf (($_) -like '*!!!*') { Write-Host (($_).Replace('!!!', '').Trim())-BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Important) } Else { Write-Host (($_).Trim())-BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Body) } } } } } # Check to see if the module version on the PowerShell Gallery does not match the local module version begin the update process (update existing module) If ($FoundModule.Version -notin $InstalledModulePreUpdate.Version) { # Ask user if they want to update the module If (!($Force)) { Do { Write-Host ('Enter ''Y'' to update the ' + $ModuleName + ' PowerShell module to the latest version or enter ''N'' to cancel:') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_UserPrompt) -NoNewline Write-Host (' ') -NoNewline $UserInput = Read-Host } Until ($UserInput.ToUpper() -in ('Y', 'N')) } Else { $UserInput = 'Y' } If ($UserInput.ToUpper() -eq 'N') { Write-Host ('Exiting the ' + $ModuleName + ' PowerShell module update process.') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Action) } Else { # Get the module config from the current module: try { $savedJCSettings = Get-JCSettingsFile -raw } catch { Write-Warning ('Could not copy JumpCloud Module Settings') } # Update the module to the latest version Write-Host ('Updating ' + $ModuleName + ' module to version: ') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Action) -NoNewline Write-Host ($FoundModule.Version) -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Body) If (-not [System.String]::IsNullOrEmpty($RepositoryCredentials)) { # SkipDependancy, we manage SDKs seperatly $InstalledModulePreUpdate | Update-PSResource -Credential $RepositoryCredentials -Repository CodeArtifact -Prerelease -Force -SkipDependencyCheck } Else { Install-Module -Repository:($Repository) -Name:($ModuleName) -RequiredVersion:($FoundModule.Version) -Force } # Remove existing module from the session if (-Not $CodeArtifact) { Get-Module -Name:($ModuleName) -ListAvailable -All | Remove-Module -Force If (!($SkipUninstallOld)) { $InstalledModulePreUpdate | ForEach-Object { Write-Host ('Uninstalling ' + $_.Name + ' module version: ') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Action) -NoNewline Write-Host (($_.Version).ToString()) -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Body) Uninstall-Module -Name:($_.Name) -RequiredVersion:($_.Version) -Force } } else { Get-Module -Name:($ModuleName) -ListAvailable -All | Remove-Module -Force } } # Uninstall previous versions # Validate install if ($CodeArtifact) { $InstalledModulePostUpdate = Get-JCInstalledModule -ModuleName:($ModuleName) -CodeArtifact } else { $InstalledModulePostUpdate = Get-JCInstalledModule -ModuleName:($ModuleName) } # Check to see if the module version on the PowerShell gallery does not match the local module version $updateCheck = If ($CodeArtifact) { if ($FoundModule.Prerelease -eq $InstalledModulePostUpdate.Prerelease) { $true } else { $false } } else { if ($FoundModule.Version -in $InstalledModulePostUpdate.Version) { $true } else { $false } } # Just compare the Major.Minor.Build Versions If ($updateCheck) { # Load new module Import-Module -Name:($ModuleName) -Scope:('Global') -Force -RequiredVersion $FoundModule.Version # Copy saved settings to new config.json if (-Not ($savedJCSettings)::IsNullOrEmpty) { # Get private settings functions: $SettingsFunctionsDir = join-path -path $ModuleRoot -childpath 'Private/settings' $regpattern = [regex]"(\/|\\)(\d+\.)?(\d+\.)?(\*|\d+)(\/|\\)" $SettingsFunctionsDir = $SettingsFunctionsDir -replace $regpattern, "/$($FoundModule.Version)/" $Private = @( Get-ChildItem -Path $SettingsFunctionsDir -Recurse) Foreach ($Import in @($Private)) { Try { # Import the functions into the session . $Import.FullName } Catch { Write-Error -Message "Failed to import function $($Import.FullName): $_" } } # update the settings file config.json Update-JCSettingsFile -settings $savedJCSettings # re-import the settings file variable $global:JCConfig = Get-JCSettingsFile } # Confirm to user module update has been successful Write-Host ('STATUS:') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Header) Write-Host ($JCColorConfig.IndentChar) -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Indentation) -NoNewline Write-Host ('The ' + $ModuleName + ' PowerShell module has successfully been updated!') -BackgroundColor:($JCColorConfig.BackgroundColor) -ForegroundColor:($JCColorConfig.ForegroundColor_Body) # function should return true if we update the module $updateStatus = $true } Else { Write-Error ("Failed to update the $($ModuleName) PowerShell module to the latest version. $($FoundModule.Version) is not in $($InstalledModulePostUpdate.Version -join ', ')") } } } } } End { if ($SDKUninstallSummary) { if ($false -in $SDKUninstallSummary.Uninstalled) { if ($PSBoundParameters.Debug -eq $true) { Write-Warning "One or more of the previous SDK modules could not be uninstalled in this session" Write-Warning "Please restart this powershell session" Write-Debug "The following modules were unabled to be uninstalled:" $SDKUninstallSummary | Format-Table | Out-Host } } else { Write-Debug "The following modules were uninstalled:" $SDKUninstallSummary | Format-Table | Out-Host } } if ($SDKResultsSummary) { If ($false -in $SDKResultsSummary.Imported) { Write-Warning "One or more of the updated SDK modules could not be imported to this session" Write-Warning "Please restart this powershell session to use the new SDK module(s)" if ($PSBoundParameters.Debug -eq $true) { $SDKResultsSummary | Format-Table | Out-Host } } else { if ($PSBoundParameters.Debug -eq $true) { Write-Debug "The following modules were sucessfully updated:" $SDKResultsSummary | Format-Table | Out-Host } } } if ($updateStatus) { Return $true } else { Return $false } } } |