PWSHModule.psm1

#region Public Functions
#region Add-PWSHModule.ps1
######## Function 1 of 11 ##################
# Function: Add-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/09 15:57:31
# ModifiedOn: 2022/08/13 09:03:01
# Synopsis: Adds a new module to the GitHub Gist List.
#############################################
 
<#
.SYNOPSIS
Adds a new module to the GitHub Gist List.
 
.DESCRIPTION
Adds a new module to the GitHub Gist List.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER ModuleName
Name of the module to add. You can also use a keyword to search for.
 
.PARAMETER Repository
Name of the Repository to hosting the module.
 
.PARAMETER RequiredVersion
This will force a version to be used. Leave blank to use the latest version.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Add-PWSHModule -ListName base -ModuleName pslauncher -Repository PSgallery -RequiredVersion 0.1.19 -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Add-PWSHModule {
    [Cmdletbinding(HelpURI = 'https://smitpi.github.io/PWSHModule/Add-PWSHModule')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string[]]$ModuleName,
        [String]$Repository = 'PSGallery',
        [string]$RequiredVersion,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID,
        [Parameter(Mandatory = $true)]
        [string]$GitHubToken
    )

    begin {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) BEGIN] Starting $($myinvocation.mycommand)"
            $headers = @{}
            $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
            $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
            $base64 = [System.Convert]::ToBase64String($bytes)
            $headers.Authorization = 'Basic {0}' -f $base64

            Write-Verbose "[$(Get-Date -Format HH:mm:ss) Starting connect to github"
            $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
            $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
            $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) Checking Config File"
            $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($ListName)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
        if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Write-Error 'Invalid Config File'}

        [System.Collections.ArrayList]$ModuleObject = @()
        $Content.Modules | ForEach-Object {[void]$ModuleObject.Add($_)}
    }
    process {
        foreach ($ModName in $ModuleName) {
            $index = 0
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Finding modules"
            $FilterMod = Find-Module -Filter $ModName -Repository $Repository | ForEach-Object {
                [PSCustomObject]@{
                    Index       = $index
                    Name        = $_.Name
                    Version     = $_.version
                    Description = $_.Description
                    ProjectURI  = $_.ProjectUri.AbsoluteUri
                }
                $index++
            }

            if ($filtermod.name.count -gt 1) {
                Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] More than one module found"
                $FilterMod | Select-Object Index, Name, Description | Format-Table -AutoSize -Wrap
                $num = Read-Host 'Index Number '
                $ModuleToAdd = $filtermod[$num]
            } elseif ($filtermod.name.Count -eq 1) {
                $ModuleToAdd = $filtermod
            } else {Write-Error 'Module not found'}

            if ($RequiredVersion) {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Looking for versions"
                    $tmp = Find-Module -Name $ModuleToAdd.name -RequiredVersion $RequiredVersion -Repository $Repository -ErrorAction Stop
                    $VersionToAdd = $RequiredVersion
                } catch {
                    $index = 0
                    Find-Module -Name $ModuleToAdd.name -AllVersions -Repository $Repository | ForEach-Object {
                        [PSCustomObject]@{
                            Index   = $index
                            Version = $_.Version
                        }
                        $index++
                    } | Tee-Object -Variable Version | Format-Table
                    $versionnum = Read-Host 'Index Number '
                    $VersionToAdd = $version[$versionnum].Version
                }
            } else {$VersionToAdd = 'Latest'}

            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Create new Object"

            if (-not($ModuleObject.Name.Contains($ModuleToAdd.Name))) {
                [void]$ModuleObject.Add([PSCustomObject]@{
                        Name        = $ModuleToAdd.Name
                        Version     = $VersionToAdd
                        Description = $ModuleToAdd.Description
                        Repository  = $Repository
                        Projecturi  = $ModuleToAdd.ProjectUri
                    })
                Write-Host '[Added]' -NoNewline -ForegroundColor Yellow; Write-Host " $($ModuleToAdd.Name)" -NoNewline -ForegroundColor Cyan; Write-Host " to $($ListName)" -ForegroundColor Green
            } else {
                Write-Host '[Duplicate]' -NoNewline -ForegroundColor DarkRed; Write-Host " $($ModuleToAdd.Name)" -NoNewline -ForegroundColor Cyan; Write-Host " to $($ListName)" -ForegroundColor Green
            }

        }
    }
    end {
        $Content.Modules = $ModuleObject | Sort-Object -Property name
        $Content.ModifiedDate = "$(Get-Date -Format u)"
        $content.ModifiedUser = "$($env:USERNAME.ToLower())@$($env:USERDNSDOMAIN.ToLower())"
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Uploading to gist"
            $Body = @{}
            $files = @{}
            $Files["$($PRGist.files.$($ListName).Filename)"] = @{content = ( $Content | ConvertTo-Json | Out-String ) }
            $Body.files = $Files
            $Uri = 'https://api.github.com/gists/{0}' -f $PRGist.id
            $json = ConvertTo-Json -InputObject $Body
            $json = [System.Text.Encoding]::UTF8.GetBytes($json)
            $null = Invoke-WebRequest -Headers $headers -Uri $Uri -Method Patch -Body $json -ErrorAction Stop
            Write-Host '[Uploaded]' -NoNewline -ForegroundColor Yellow; Write-Host " List: $($ListName)" -NoNewline -ForegroundColor Cyan; Write-Host ' to Github Gist' -ForegroundColor Green
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
    }
} #end Function


$scriptblock = {
    (Get-PSRepository).Name
}
Register-ArgumentCompleter -CommandName Add-PWSHModule -ParameterName Repository -ScriptBlock $scriptBlock


$scriptblock2 = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Add-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock2
 
Export-ModuleMember -Function Add-PWSHModule
#endregion
 
#region Add-PWSHModuleDefaultsToProfile.ps1
######## Function 2 of 11 ##################
# Function: Add-PWSHModuleDefaultsToProfile
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/31 11:51:50
# ModifiedOn: 2022/07/31 14:18:41
# Synopsis: Creates PSDefaultParameterValues in the users profile files.
#############################################
 
<#
.SYNOPSIS
Creates PSDefaultParameterValues in the users profile files.
 
.DESCRIPTION
Creates PSDefaultParameterValues in the users profile files.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.PARAMETER Scope
Where the module will be installed. AllUsers require admin access.
 
.EXAMPLE
Add-PWSHModuleDefaultsToProfile -GitHubUserID smitpi -PublicGist -Scope AllUsers
 
#>

Function Add-PWSHModuleDefaultsToProfile {
    [Cmdletbinding(DefaultParameterSetName = 'Public', HelpURI = 'https://smitpi.github.io/PWSHModule/Add-PWSHModuleDefaultsToProfile')]
    [OutputType([System.Object[]])]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken,
        [ValidateSet('AllUsers', 'CurrentUser')]
        [string]$Scope
    )

    if ($PublicGist) {
        $Script:PSDefaultParameterValues['*PWSHModule*:GitHubUserID'] = "$($GitHubUserID)"
        $Script:PSDefaultParameterValues['*PWSHModule*:PublicGist'] = $true
        $Script:PSDefaultParameterValues['*PWSHModule*:Scope'] = "$($Scope)"

        $ToAppend = @"
 
#region PWSHModule Defaults
`$PSDefaultParameterValues['*PWSHModule*:GitHubUserID'] = "$($GitHubUserID)"
`$PSDefaultParameterValues['*PWSHModule*:PublicGist'] = `$true
`$PSDefaultParameterValues['*PWSHModule*:Scope'] = "$($Scope)"
#endregion PWSHModule
"@

    } else {
        $Script:PSDefaultParameterValues['*PWSHModule*:GitHubUserID'] = "$($GitHubUserID)"
        $Script:PSDefaultParameterValues['*PWSHModule*:GitHubToken'] = "$($GitHubToken)"
        $Script:PSDefaultParameterValues['*PWSHModule*:Scope'] = "$($Scope)"
        $ToAppend = @"
         
#region PWSHModule Defaults
`$PSDefaultParameterValues['*PWSHModule*:GitHubUserID'] = "$($GitHubUserID)"
`$PSDefaultParameterValues['*PWSHModule*:GitHubToken'] = "$($GitHubToken)"
`$PSDefaultParameterValues['*PWSHModule*:Scope'] = "$($Scope)"
#endregion PWSHModule
"@

    }

    try {
        $CheckProfile = Get-Item $PROFILE -ErrorAction Stop
    } catch { $CheckProfile = New-Item $PROFILE -ItemType File -Force}
    
    $Files = Get-ChildItem -Path "$($CheckProfile.Directory)\*profile*"
    foreach ($file in $files) {    
        $tmp = Get-Content -Path $file.FullName | Where-Object { $_ -notlike '*PWSHModule*'}
        $tmp | Set-Content -Path $file.FullName -Force
        Add-Content -Value $ToAppend -Path $file.FullName -Force -Encoding utf8
        Write-Host '[Updated]' -NoNewline -ForegroundColor Yellow; Write-Host ' Profile File:' -NoNewline -ForegroundColor Cyan; Write-Host " $($file.FullName)" -ForegroundColor Green
    }

} #end Function
 
Export-ModuleMember -Function Add-PWSHModuleDefaultsToProfile
#endregion
 
#region Install-PWSHModule.ps1
######## Function 3 of 11 ##################
# Function: Install-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/12 07:38:48
# ModifiedOn: 2022/08/17 12:47:39
# Synopsis: Install modules from the specified list.
#############################################
 
<#
.SYNOPSIS
Install modules from the specified list.
 
.DESCRIPTION
Install modules from the specified list.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER Scope
Where the module will be installed. AllUsers require admin access.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Install-PWSHModule -Filename extended -Scope CurrentUser -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Install-PWSHModule {
    [Cmdletbinding(DefaultParameterSetName = 'Private', HelpURI = 'https://smitpi.github.io/PWSHModule/Install-PWSHModule')]
    PARAM(
        [Parameter(Position = 0)]
        [string]$ListName,
        [Parameter(Position = 1)]
        [ValidateSet('AllUsers', 'CurrentUser')]
        [string]$Scope,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID,
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken
    )

    if ($scope -like 'AllUsers') {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) BEGIN] Check for admin"
     $IsAdmin = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
        if (-not($IsAdmin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { Write-Error 'Must be running an elevated prompt.' }
    }

    try {
        if ($PublicGist) {
            Write-Host '[Using] ' -NoNewline -ForegroundColor Yellow 
            Write-Host 'Public Gist:' -NoNewline -ForegroundColor Cyan 
            Write-Host ' for list:' -ForegroundColor Green -NoNewline 
            Write-Host "$($ListName)" -ForegroundColor Cyan
        }

        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connect to Gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking Config File"
        $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($ListName)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
    if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Write-Error 'Invalid Config File'}


    $InstallModuleSettings = @{
        AllowClobber       = $true
        Force              = $true
        SkipPublisherCheck = $true
        AllowPrerelease    = $true
    }

    foreach ($module in $Content.Modules) {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking for installed module"
        if ($module.Version -like 'Latest') {
            $mod = Get-Module -Name $module.Name
            if (-not($mod)) {$mod = Get-Module -Name $module.name -ListAvailable}
            if (-not($mod)) { 
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Installing module"
                    Write-Host '[Installing] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)" -ForegroundColor Green -NoNewline ; Write-Host ' to scope: ' -ForegroundColor DarkRed -NoNewline ; Write-Host "$($scope)" -ForegroundColor Cyan
                    Install-Module -Name $module.Name -Repository $module.Repository -Scope $Scope @InstallModuleSettings
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            } else {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking versions"
                    Write-Host '[Installed] ' -NoNewline -ForegroundColor Green ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name) " -ForegroundColor Green -NoNewline ; Write-Host "$($mod.Path)" -ForegroundColor DarkRed
                    $OnlineMod = Find-Module -Name $module.name -Repository $module.Repository
                    [version]$Onlineversion = $OnlineMod.version 
                    [version]$Localversion = ($mod | Sort-Object -Property Version -Descending)[0].Version
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                if ($Localversion -lt $Onlineversion) {
                    Write-Host "`t[Upgrading] " -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)" -ForegroundColor Green -NoNewline; Write-Host " v$($OnlineMod.version)" -ForegroundColor DarkRed
                    try {
                        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Updating module"
                        Update-Module -Name $module.Name -Force -ErrorAction Stop
                    } catch {
                        try {
                            Install-Module -Name $module.name -Scope $Scope -Repository $module.Repository @InstallModuleSettings
                        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                    }
                    Get-Module $module.name -ListAvailable | Remove-Module -Force -ErrorAction SilentlyContinue
                    $mods = (Get-Module $module.name -ListAvailable | Sort-Object -Property version -Descending) | Select-Object -Skip 1
                    foreach ($mod in $mods) {
                        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] UnInstalling module"
                        Write-Host "`t[Uninstalling] " -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)($($mod.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($mod.Path)" -ForegroundColor DarkRed
                        try {
                            Uninstall-Module -Name $mod.name -RequiredVersion $mod.Version -Force
                        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                    }
                }
            }
        } else {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking installed module"
            $mod = Get-Module -Name $module.Name
            if (-not($mod)) {$mod = Get-Module -Name $module.name -ListAvailable}
            if ((-not($mod)) -or $mod.Version -lt $module.Version) {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Installing module"
                    Write-Host '[Installing] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)($($module.Version))" -ForegroundColor Green -NoNewline ; Write-Host ' to scope: ' -ForegroundColor DarkRed -NoNewline ; Write-Host "$($scope)" -ForegroundColor Cyan
                    Install-Module -Name $module.Name -Repository $module.Repository -RequiredVersion $module.Version -Scope $Scope @InstallModuleSettings
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            } else {
                Write-Host '[Installed] ' -NoNewline -ForegroundColor Green ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name) " -ForegroundColor Green -NoNewline ; Write-Host "$($mod.Path)" -ForegroundColor DarkRed
            }
        }
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
    }
} #end Function


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Install-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock
 
Export-ModuleMember -Function Install-PWSHModule
#endregion
 
#region Move-PWSHModuleBetweenScope.ps1
######## Function 4 of 11 ##################
# Function: Move-PWSHModuleBetweenScope
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/08/20 12:38:44
# ModifiedOn: 2022/08/20 23:29:54
# Synopsis: Moves modules between scopes (CurrentUser and AllUsers).
#############################################
 
<#
.SYNOPSIS
Moves modules between scopes (CurrentUser and AllUsers).
 
.DESCRIPTION
Moves modules between scopes (CurrentUser and AllUsers).
 
.PARAMETER SourceScope
From where the modules will be copied.
 
.PARAMETER DestinationScope
To there the modules will be copied.
 
.PARAMETER ModuleName
Name of the modules to move. You can select multiple names or you can use * to select all.
 
.PARAMETER PSRepository
The repository will be used to install the module at the destination.
 
.EXAMPLE
Move-PWSHModuleBetweenScope -SourceScope D:\Documents\PowerShell\Modules -DestinationScope C:\Program Files\PowerShell\Modules -ModuleName PWSHMOdule -PSRepository psgallery
 
#>

Function Move-PWSHModuleBetweenScope {
    [Cmdletbinding(DefaultParameterSetName = 'Set1', HelpURI = 'https://smitpi.github.io/PWSHModule/Move-PWSHModuleBetweenScope')]
    [OutputType([System.Object[]])]
    PARAM(
        [Parameter(Mandatory = $true)]
        [ValidateScript( { $IsAdmin = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
                if ($IsAdmin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $True }
                else { Throw 'Must be running an elevated prompt.' } })]
        [System.IO.DirectoryInfo]$SourceScope,

        [Parameter(Mandatory = $true)]
        [System.IO.DirectoryInfo]$DestinationScope,

        [Parameter(ValueFromPipeline, Mandatory)]
        [Alias('Name')]
        [ValidateScript( { if (Get-Module -Name $_ -ListAvailable) { $True }
                else { Throw 'Module not found.' } })]
        [string[]]$ModuleName,

        [string]$PSRepository = 'PSGallery'
    )

    foreach ($mod in $ModuleName) {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking for installed module $($mod)"
        try {
            $MoveMod = Get-Module -Name $mod -ListAvailable -ErrorAction Stop | Where-Object {$_.path -like "$($SourceScope)*"} | Sort-Object -Property Version -Descending | Select-Object -First 1
        } catch {Write-Warning "Did not find $($ModuleName) in $($SourceScope)"}
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) Saving] Module $($MoveMod.name)"
            Write-Host '[Moving] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($MoveMod.Name)($($MoveMod.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($DestinationScope)" -ForegroundColor DarkRed            
            Save-Module -Name $MoveMod.Name -RequiredVersion $MoveMod.Version -Repository $PSRepository -Force -AllowPrerelease -AcceptLicense -Path (Get-Item $DestinationScope).FullName -ErrorAction Stop
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) Uninstalling] Module $($MoveMod.name)"
            Write-Host "`t[Deleteing] " -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($MoveMod.Name)($($MoveMod.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($SourceScope)" -ForegroundColor DarkRed            
            if (Test-Path (Join-Path $DestinationScope -ChildPath "$($MoveMod.Name)\$($MoveMod.Version)")) {
                Join-Path -Path (Get-Item $MoveMod.Path) -ChildPath '..\..' -Resolve -ErrorAction Stop | Remove-Item -Recurse -Force
            } else {Write-Warning 'Move failed, leaving source directory.'}
        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) Complete"
    }
} #end Function
$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $env:PSModulePath.Split(';') | ForEach-Object {"""$($_)"""}
}
Register-ArgumentCompleter -CommandName Move-PWSHModuleBetweenScope -ParameterName SourceScope -ScriptBlock $scriptBlock
Register-ArgumentCompleter -CommandName Move-PWSHModuleBetweenScope -ParameterName DestinationScope -ScriptBlock $scriptBlock

$scriptblock2 = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    (@([IO.Path]::Combine("$([Environment]::GetFolderPath('MyDocuments'))", 'WindowsPowerShell', 'Modules'),
        [IO.Path]::Combine("$([Environment]::GetFolderPath('MyDocuments'))", 'PowerShell', 'Modules'),
        [IO.Path]::Combine("$($env:ProgramFiles)", 'WindowsPowerShell', 'Modules'),
        [IO.Path]::Combine("$($env:ProgramFiles)", 'PowerShell', 'Modules')) | Get-ChildItem -Directory).Name | Sort-Object -Unique    
}
Register-ArgumentCompleter -CommandName Move-PWSHModuleBetweenScope -ParameterName ModuleName -ScriptBlock $scriptBlock2

$scriptblock3 = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    Get-PSRepository | ForEach-Object {$_.name}
}
Register-ArgumentCompleter -CommandName Move-PWSHModuleBetweenScope -ParameterName PSRepository -ScriptBlock $scriptBlock3
 
Export-ModuleMember -Function Move-PWSHModuleBetweenScope
#endregion
 
#region New-PWSHModuleList.ps1
######## Function 5 of 11 ##################
# Function: New-PWSHModuleList
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/09 15:22:20
# ModifiedOn: 2022/08/13 09:03:59
# Synopsis: Add a new list to GitHub Gist.
#############################################
 
<#
.SYNOPSIS
Add a new list to GitHub Gist.
 
.DESCRIPTION
Add a new list to GitHub Gist.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER Description
Summary of the function for the list.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
New-PWSHModuleList -ListName Base -Description "These modules needs to be installed on all servers" -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function New-PWSHModuleList {
    [Cmdletbinding( HelpURI = 'https://smitpi.github.io/PWSHModule/New-PWSHModuleList')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [Parameter(Mandatory = $true)]
        [string]$Description,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(Mandatory = $true)]
        [string]$GitHubToken
    )

    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Creating config"
    $NewConfig = [PSCustomObject]@{
        CreateDate   = (Get-Date -Format u)
        Description  = $Description
        Author       = "$($env:USERNAME.ToLower())@$($env:USERDNSDOMAIN.ToLower())"
        ModifiedDate = 'Unknown'
        ModifiedUser = 'Unknown'
        Modules      = [PSCustomObject]@{
            Name        = 'PWSHModule'
            Version     = 'Latest'
            Description = 'Uses a GitHub Gist File to install and maintain a list of PowerShell Modules'
            Repository  = 'PSGallery'
            Projecturi  = 'https://github.com/smitpi/PWSHModule'
        }
 } | ConvertTo-Json

    $ConfigFile = Join-Path $env:TEMP -ChildPath "$($ListName).json"
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Create temp file"
    if (Test-Path $ConfigFile) {
        Write-Warning "Config File exists, Renaming file to $($ListName)-$(Get-Date -Format yyyyMMdd_HHmm).json"    
        try {
            Rename-Item $ConfigFile -NewName "$($ListName)-$(Get-Date -Format yyyyMMdd_HHmm).json" -Force -ErrorAction Stop | Out-Null
        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message);exit"}
    }
    try {
        $NewConfig | Set-Content -Path $ConfigFile -Encoding utf8 -ErrorAction Stop
    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}


    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connecting to Gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

        
    if ([string]::IsNullOrEmpty($PRGist)) {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Uploading to gist"
            $Body = @{}
            $files = @{}
            $Files["$($ListName)"] = @{content = ( Get-Content (Get-Item $ConfigFile).FullName -Encoding UTF8 | Out-String ) }
            $Body.files = $Files
            $Body.description = 'PWSHModule-ConfigFile'
            $json = ConvertTo-Json -InputObject $Body
            $json = [System.Text.Encoding]::UTF8.GetBytes($json)
            $null = Invoke-WebRequest -Headers $headers -Uri https://api.github.com/gists -Method Post -Body $json -ErrorAction Stop
            Write-Host '[Uploaded]' -NoNewline -ForegroundColor Yellow; Write-Host " $($ListName).json" -NoNewline -ForegroundColor Cyan; Write-Host ' to Github Gist' -ForegroundColor Green

        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}
    } else {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Uploading to Gist"
            $Body = @{}
            $files = @{}
            $Files["$($ListName)"] = @{content = ( Get-Content (Get-Item $ConfigFile).FullName -Encoding UTF8 | Out-String ) }
            $Body.files = $Files
            $Uri = 'https://api.github.com/gists/{0}' -f $PRGist.id
            $json = ConvertTo-Json -InputObject $Body
            $json = [System.Text.Encoding]::UTF8.GetBytes($json)
            $null = Invoke-WebRequest -Headers $headers -Uri $Uri -Method Patch -Body $json -ErrorAction Stop
            Write-Host '[Uploaded]' -NoNewline -ForegroundColor Yellow; Write-Host " $($ListName).json" -NoNewline -ForegroundColor Cyan; Write-Host ' to Github Gist' -ForegroundColor Green
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}
    }
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"


} #end Function
 
Export-ModuleMember -Function New-PWSHModuleList
#endregion
 
#region Remove-PWSHModule.ps1
######## Function 6 of 11 ##################
# Function: Remove-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/13 11:14:06
# ModifiedOn: 2022/08/13 09:04:25
# Synopsis: Remove module from the specified list.
#############################################
 
<#
.SYNOPSIS
Remove module from the specified list.
 
.DESCRIPTION
Remove module from the specified list.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER ModuleName
Module to remove.
 
.PARAMETER ForceUninstallModules
Will uninstall the modules as well.
 
.EXAMPLE
Remove-PWSHModule -ListName base -ModuleName pslauncher -GitHubUserID smitpi -GitHubToken $GitHubToken
#>

Function Remove-PWSHModule {
    [Cmdletbinding(SupportsShouldProcess = $true, HelpURI = 'https://smitpi.github.io/PWSHModule/Remove-PWSHModule')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID,
        [Parameter(Mandatory = $true)]
        [string]$GitHubToken,
        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string[]]$ModuleName,
        [ValidateScript( { $IsAdmin = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
                if ($IsAdmin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $True }
                else { Throw 'Must be running an elevated prompt.' } })]
        [switch]$ForceUninstallModules
    )
    begin {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connecting to Gist"
            $headers = @{}
            $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
            $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
            $base64 = [System.Convert]::ToBase64String($bytes)
            $headers.Authorization = 'Basic {0}' -f $base64

            $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
            $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
            $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking config file."
            $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($ListName)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
        if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Write-Error 'Invalid Config File'}

        [System.Collections.ArrayList]$ModuleObject = @()
        $Content.Modules | ForEach-Object {[void]$ModuleObject.Add($_)
        }
    }
    process {
        if ($pscmdlet.ShouldProcess('Target', 'Operation')) {
            foreach ($mod in $ModuleName) {
                Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Find module to remove"
                $Modremove = ($Content.Modules | Where-Object {$_.Name -like "*$Mod*"})
                if ([string]::IsNullOrEmpty($Modremove) -or ($Modremove.name.count -gt 1)) {
                    Write-Error 'Module not found. Redevine your search'
                } else {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Removing module"
                    $ModuleObject.Remove($Modremove)
                    Write-Host '[Removed]' -NoNewline -ForegroundColor Yellow; Write-Host " $($Modremove.Name)" -NoNewline -ForegroundColor Cyan; Write-Host " from $($ListName)" -ForegroundColor Green
                    if ($ForceUninstallModules) {
                        try {
                            Write-Host '[Uninstalling]' -NoNewline -ForegroundColor Yellow ; Write-Host 'All Versions of Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($mod) " -ForegroundColor Green
                            Uninstall-Module -Name $mod -AllVersions -Force -ErrorAction Stop
                        } catch {
                            Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"
                            Get-Module -Name $Mod -ListAvailable | ForEach-Object {
                                try {
                                    Write-Host '[Deleting] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($_.Name)($($_.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($_.Path)" -ForegroundColor DarkRed
                                    $Directory = Join-Path -Path (Get-Item $_.Path).FullName -ChildPath '..\..\' -Resolve
                                    Remove-Item -Path $Directory -Recurse -Force -ErrorAction Stop
                                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                            }
                        }
                    }
                }
            }
        }
    }
    end {
        try {
            $Content.Modules = $ModuleObject | Sort-Object -Property name
            $Content.ModifiedDate = "$(Get-Date -Format u)"
            $content.ModifiedUser = "$($env:USERNAME.ToLower())@$($env:USERDNSDOMAIN.ToLower())"
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Uploading to gist"
            $Body = @{}
            $files = @{}
            $Files["$($PRGist.files.$($ListName).Filename)"] = @{content = ( $Content | ConvertTo-Json | Out-String ) }
            $Body.files = $Files
            $Uri = 'https://api.github.com/gists/{0}' -f $PRGist.id
            $json = ConvertTo-Json -InputObject $Body
            $json = [System.Text.Encoding]::UTF8.GetBytes($json)
            $null = Invoke-WebRequest -Headers $headers -Uri $Uri -Method Patch -Body $json -ErrorAction Stop
            Write-Host '[Uploaded] ' -NoNewline -ForegroundColor Yellow; Write-Host " List: $($ListName)" -NoNewline -ForegroundColor Cyan; Write-Host ' to Github Gist' -ForegroundColor Green
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}
    }
} #end Function


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Remove-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock
 
Export-ModuleMember -Function Remove-PWSHModule
#endregion
 
#region Remove-PWSHModuleList.ps1
######## Function 7 of 11 ##################
# Function: Remove-PWSHModuleList
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/31 11:14:51
# ModifiedOn: 2022/08/13 09:04:57
# Synopsis: Deletes a list from GitHub Gist
#############################################
 
<#
.SYNOPSIS
Deletes a list from GitHub Gist
 
.DESCRIPTION
Deletes a list from GitHub Gist
 
.PARAMETER ListName
The Name of the list to remove.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Remove-PWSHModuleList -ListName Base -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Remove-PWSHModuleList {
    [Cmdletbinding(HelpURI = 'https://smitpi.github.io/PWSHModule/Remove-PWSHModuleList')]
    [OutputType([System.Object[]])]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(Mandatory = $true)]
        [string]$GitHubToken
    )

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connect to gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {throw "Can't connect to gist:`n $($_.Exception.Message)"}


    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Create object"
    $CheckExist = $PRGist.files | Get-Member -MemberType NoteProperty | Where-Object {$_.name -like $ListName}
    if (-not([string]::IsNullOrEmpty($CheckExist))) {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Remove list from Gist"
            $Body = @{}
            $files = @{}
            $Files["$($ListName)"] = $null
            $Body.files = $Files
            $Uri = 'https://api.github.com/gists/{0}' -f $PRGist.id
            $json = ConvertTo-Json -InputObject $Body
            $json = [System.Text.Encoding]::UTF8.GetBytes($json)
            $null = Invoke-WebRequest -Headers $headers -Uri $Uri -Method Patch -Body $json -ErrorAction Stop
            Write-Host '[Removed]' -NoNewline -ForegroundColor Yellow; Write-Host " $($ListName)" -NoNewline -ForegroundColor Cyan; Write-Host ' from Github Gist' -ForegroundColor DarkRed
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] updated gist."
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}
    }
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
} #end Function

$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Remove-PWSHModuleList -ParameterName ListName -ScriptBlock $scriptBlock
 
Export-ModuleMember -Function Remove-PWSHModuleList
#endregion
 
#region Save-PWSHModule.ps1
######## Function 8 of 11 ##################
# Function: Save-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/13 10:26:41
# ModifiedOn: 2022/08/13 09:05:24
# Synopsis: Saves the modules from the specified list to a folder.
#############################################
 
<#
.SYNOPSIS
Saves the modules from the specified list to a folder.
 
.DESCRIPTION
Saves the modules from the specified list to a folder.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER AsNuGet
Save in the NuGet format
 
.PARAMETER Path
Where to save
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Save-PWSHModule -ListName extended -AsNuGet -Path c:\temp\ -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Save-PWSHModule {
    [Cmdletbinding(DefaultParameterSetName = 'Private', HelpURI = 'https://smitpi.github.io/PWSHModule/Save-PWSHModule')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [switch]$AsNuGet,
        [ValidateScript( { if (Test-Path $_) { $true }
                else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true }
            })]
        [System.IO.DirectoryInfo]$Path = 'C:\Temp',
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken
    )

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connect to Gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking Config File"
        $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($ListName)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
    if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Write-Error 'Invalid Config File'}

    foreach ($module in $Content.Modules) {
        if ($module.Version -like 'Latest') {
            if ($AsNuGet) {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Downloading"
                    Write-Host '[Downloading] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'NuGet: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name) " -ForegroundColor Green -NoNewline ; Write-Host "Path: $($Path)" -ForegroundColor DarkRed
                    Save-Package -Name $module.Name -Provider NuGet -Source (Get-PSRepository -Name $module.Repository).SourceLocation -Path $Path | Out-Null
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            } else {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Downloading"
                    Write-Host '[Downloading] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name) " -ForegroundColor Green -NoNewline ; Write-Host "Path: $($Path)" -ForegroundColor DarkRed
                    Save-Module -Name $module.name -Repository $module.Repository -Path $Path
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            }
        } else {
            if ($AsNuGet) {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Downloading"
                    Write-Host '[Downloading] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'NuGet: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)(ver $($module.version)) " -ForegroundColor Green -NoNewline ; Write-Host "Path: $($Path)" -ForegroundColor DarkRed
                    Save-Package -Name $module.Name -Provider NuGet -Source (Get-PSRepository -Name $module.Repository).SourceLocation -RequiredVersion $module.Version -Path $Path | Out-Null
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            } else {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Downloading"
                    Write-Host '[Downloading] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)(ver $($module.version)) " -ForegroundColor Green -NoNewline ; Write-Host "Path: $($Path)" -ForegroundColor DarkRed
                    Save-Module -Name $module.name -Repository $module.Repository -RequiredVersion $module.Version -Path $Path
                } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
            }

        }
    }
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
} #end Function


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Save-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock
 
Export-ModuleMember -Function Save-PWSHModule
#endregion
 
#region Show-PWSHModule.ps1
######## Function 9 of 11 ##################
# Function: Show-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/09 15:57:20
# ModifiedOn: 2022/08/13 09:05:45
# Synopsis: Show the details of the modules in a list.
#############################################
 
<#
.SYNOPSIS
Show the details of the modules in a list.
 
.DESCRIPTION
Show the details of the modules in a list.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER CompareInstalled
Compare the list to what is installed.
 
.PARAMETER ShowProjectURI
Will open the browser to the the project URL.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Show-PWSHModule -ListName Base -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Show-PWSHModule {
    [Cmdletbinding(DefaultParameterSetName = 'Private', HelpURI = 'https://smitpi.github.io/PWSHModule/Show-PWSHModule')]
    PARAM(        [Parameter(Mandatory = $true)]
        [string]$ListName,
        [switch]$CompareInstalled,
        [switch]$ShowProjectURI,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken
    )

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connecting to Gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {throw "Can't connect to gist:`n $($_.Exception.Message)"}

    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking config file"
        $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($Listname)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
    if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Throw 'Invalid Config File'}

    $index = 0
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Creating object"
    [System.Collections.ArrayList]$ModuleObject = @()        
    $Content.Modules | ForEach-Object {                
        [void]$ModuleObject.Add([PSCustomObject]@{
                Index       = $index
                Name        = $_.Name
                Version     = $_.version
                Description = $_.Description
                Repository  = $_.Repository
                Projecturi  = $_.projecturi
            })
        $index++
    }

    if ($CompareInstalled) {
        [System.Collections.ArrayList]$CompareObject = @()        
        $index = 0
        foreach ($CompareModule in $ModuleObject) {
            try {
                Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Online: $($CompareModule.name)"
                if ($CompareModule.Version -like 'Latest') {
                    $online = Find-Module -Name $CompareModule.name -Repository $CompareModule.Repository 
                } else {
                    $online = Find-Module -Name $CompareModule.name -Repository $CompareModule.Repository -RequiredVersion $CompareModule.Version
                }
                Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Local: $($CompareModule.name)"
                $local = $null
                $local = Get-Module -Name $CompareModule.Name -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
                if ([string]::IsNullOrEmpty($local)) {
                    $InstallVer = 'NotInstalled'
                    $InstallCount = 'NotInstalled'
                    $InstallFolder = 'NotInstalled'
                } else {
                    $InstallVer = $local.Version
                    $InstallCount = (Get-Module -Name $CompareModule.Name -ListAvailable).count
                    $InstallFolder = (Get-Item $local.Path).DirectoryName
                }
                if ($local.Version -lt $online.Version) {$update = $true}
                else {$update = $false}
                Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Building List with module: $($CompareModule.name)"
                [void]$CompareObject.Add([PSCustomObject]@{
                        Index           = $index
                        Name            = $CompareModule.Name
                        InstalledVer    = $InstallVer
                        OnlineVer       = $online.Version
                        UpdateAvailable = $update
                        InstallCount    = $InstallCount
                        Folder          = $InstallFolder
                        Description     = $CompareModule.Description
                        Repository      = $CompareModule.Repository
                    })
            } catch {Write-Warning "Error $($CompareModule.Name): `n`tMessage:$($_.Exception.Message)"}
            $index++
        }
        $CompareObject
    } else {$ModuleObject}
    if ($ShowProjectURI) {
        Write-Output ' '
        [int]$IndexURI = Read-Host 'Module Index Number'
        if (-not([string]::IsNullOrEmpty($Content.Modules[$IndexURI].projecturi))) {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] open url"
            Start-Process "$($Content.Modules[$IndexURI].projecturi)"
        } else { Write-Warning 'NotInstalled ProjectURI'}
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
    }
} #end Function


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if ([bool]($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Show-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock
 
Export-ModuleMember -Function Show-PWSHModule
#endregion
 
#region Show-PWSHModuleList.ps1
######## Function 10 of 11 ##################
# Function: Show-PWSHModuleList
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/13 01:15:39
# ModifiedOn: 2022/08/13 09:06:01
# Synopsis: List all the GitHub Gist Lists.
#############################################
 
<#
.SYNOPSIS
List all the GitHub Gist Lists.
 
.DESCRIPTION
List all the GitHub Gist Lists.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Show-PWSHModuleList -GitHubUserID smitpi -GitHubToken $GitHubToken
 
#>

Function Show-PWSHModuleList {
    [Cmdletbinding(DefaultParameterSetName = 'Private', HelpURI = 'https://smitpi.github.io/PWSHModule/Show-PWSHModuleList')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID, 
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken
    )

    
    try {
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connect to gist"
        $headers = @{}
        $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
        $base64 = [System.Convert]::ToBase64String($bytes)
        $headers.Authorization = 'Basic {0}' -f $base64

        $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
        $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
        $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
    } catch {throw "Can't connect to gist:`n $($_.Exception.Message)"}


    [System.Collections.ArrayList]$GistObject = @()
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Create object"
    $PRGist.files | Get-Member -MemberType NoteProperty | ForEach-Object {
        $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($_.name)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
        if ($Content.modifiedDate -notlike 'Unknown') {
            $modifiedDate = [datetime]$Content.ModifiedDate
            $modifiedUser = $Content.ModifiedUser
        } else { 
            $modifiedDate = 'Unknown'
            $modifiedUser = 'Unknown'
        }
        [void]$GistObject.Add([PSCustomObject]@{
                Name         = $_.Name
                Description  = $Content.Description
                Date         = [datetime]$Content.CreateDate
                Author       = $Content.Author
                ModifiedDate = $modifiedDate
                ModifiedUser = $modifiedUser
            })
    }

    $GistObject
    Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"

} #end Function
 
Export-ModuleMember -Function Show-PWSHModuleList
#endregion
 
#region Uninstall-PWSHModule.ps1
######## Function 11 of 11 ##################
# Function: Uninstall-PWSHModule
# Module: PWSHModule
# ModuleVersion: 0.1.17
# Author: Pierre Smit
# Company: HTPCZA Tech
# CreatedOn: 2022/07/20 19:06:13
# ModifiedOn: 2022/08/20 13:52:57
# Synopsis: Will uninstall the module from the system.
#############################################
 
<#
.SYNOPSIS
Will uninstall the module from the system.
 
.DESCRIPTION
Will uninstall the module from the system. Select OldVersions to remove duplicates only.
 
.PARAMETER ListName
The File Name on GitHub Gist.
 
.PARAMETER ModuleName
Name of the module to uninstall. Use * to select all modules in the list.
 
.PARAMETER OldVersions
Will only uninstall old versions of the module.
 
.PARAMETER ForceDeleteFolder
Will force delete the base folder.
 
.PARAMETER GitHubUserID
The GitHub User ID.
 
.PARAMETER PublicGist
Select if the list is hosted publicly.
 
.PARAMETER GitHubToken
GitHub Token with access to the Users' Gist.
 
.EXAMPLE
Uninstall-PWSHModule -ListName base -OldVersions -GitHubUserID smitpi -PublicGist
 
#>

Function Uninstall-PWSHModule {
    [Cmdletbinding(DefaultParameterSetName = 'Private', HelpURI = 'https://smitpi.github.io/PWSHModule/Install-PWSHModule')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [ValidateScript( { $IsAdmin = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
                if ($IsAdmin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $True }
                else { Throw 'Must be running an elevated prompt.' } })]
        [string]$ListName,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string[]]$ModuleName = "*",
        [switch]$OldVersions,
        [switch]$ForceDeleteFolder,
        [Parameter(Mandatory = $true)]
        [string]$GitHubUserID,
        [Parameter(ParameterSetName = 'Public')]
        [switch]$PublicGist,
        [Parameter(ParameterSetName = 'Private')]
        [string]$GitHubToken
    )

    begin {
        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Connect to Gist"
            $headers = @{}
            $auth = '{0}:{1}' -f $GitHubUserID, $GitHubToken
            $bytes = [System.Text.Encoding]::ASCII.GetBytes($auth)
            $base64 = [System.Convert]::ToBase64String($bytes)
            $headers.Authorization = 'Basic {0}' -f $base64

            $url = 'https://api.github.com/users/{0}/gists' -f $GitHubUserID
            $AllGist = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
            $PRGist = $AllGist | Select-Object | Where-Object { $_.description -like 'PWSHModule-ConfigFile' }
        } catch {Write-Error "Can't connect to gist:`n $($_.Exception.Message)"}

        try {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking Config File"
            $Content = (Invoke-WebRequest -Uri ($PRGist.files.$($ListName)).raw_url -Headers $headers).content | ConvertFrom-Json -ErrorAction Stop
        } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
        if ([string]::IsNullOrEmpty($Content.CreateDate) -or [string]::IsNullOrEmpty($Content.Modules)) {Write-Error 'Invalid Config File'}
        [System.Collections.ArrayList]$CollectObject = @()
    }
    process {
        
        foreach ($collectmod in $ModuleName) {
            $Content.Modules | Where-Object {$_.name -like $collectmod} | ForEach-Object {[void]$CollectObject.Add($_)}
        }
        #$mods = Get-Module -list | Where-Object path -NotMatch 'windows\\system32' | Group-Object -Property name | Where-Object count -GT 1
        #$mods | ForEach-Object { $_.group | Sort-Object -Property version -Descending| Select-Object -Skip 1 } | ForEach-Object { Uninstall-Module -Name $_.name -RequiredVersion $_.version -WhatIf }
    }
    end {
        foreach ($module in $CollectObject) {
            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Checking for installed module"
            if ($OldVersions) {
                Get-Module $module.name -ListAvailable | Remove-Module -Force -ErrorAction SilentlyContinue
                $mods = (Get-Module $module.name -ListAvailable | Sort-Object -Property version -Descending) | Select-Object -Skip 1
                foreach ($mod in $mods) {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] UnInstalling module"
                    Write-Host '[Uninstalling] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)($($mod.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($mod.Path)" -ForegroundColor DarkRed
                    try {
                        Uninstall-Module -Name $mod.name -RequiredVersion $mod.Version -Force
                    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                }
            } elseif ($OldVersions -and $ForceDeleteFolder) {
                Get-Module $module.name -ListAvailable | Remove-Module -Force -ErrorAction SilentlyContinue
                $mods = (Get-Module $module.name -ListAvailable | Sort-Object -Property version -Descending) | Select-Object -Skip 1
                foreach ($mod in $mods) {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Deleting Folder"
                    Write-Host '[Deleting] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name)($($mod.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($mod.Path)" -ForegroundColor DarkRed
                    try {
                        $folder = Get-Module -Name $mod.name -ListAvailable | Where-Object {$_.version -like $mod.version}
                        join-path -Path (get-item $folder.Path) -ChildPath "..\.." -Resolve -ErrorAction Stop | Remove-Item -Recurse -Force                        
                    } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                }
            } else {
                try {
                    Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Uninstalling module $($module.Name)"
                    Write-Host '[Uninstalling]' -NoNewline -ForegroundColor Yellow ; Write-Host 'All Versions of Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($module.Name) " -ForegroundColor Green
                    Uninstall-Module -Name $module.Name -AllVersions -Force -ErrorAction Stop
                } catch { Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                if ($ForceDeleteFolder) {
                    Get-Module -Name $Module.name -ListAvailable | ForEach-Object {
                            Write-Verbose "[$(Get-Date -Format HH:mm:ss) PROCESS] Force Delete module $($module.Name)"
                            Write-Host '[Deleting] ' -NoNewline -ForegroundColor Yellow ; Write-Host 'Module: ' -NoNewline -ForegroundColor Cyan ; Write-Host "$($_.Name)($($_.Version)) " -ForegroundColor Green -NoNewline ; Write-Host "$($_.Path)" -ForegroundColor DarkRed
                            try {
                                Join-Path -Path (Get-Item $_.Path).FullName -ChildPath '..\..\' -Resolve |  Remove-Item -Recurse -Force -ErrorAction Stop
                            } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"}
                    }
                }
            }
        }
        Write-Verbose "[$(Get-Date -Format HH:mm:ss) DONE]"
    }
} #end Function


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if (($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {(Show-PWSHModuleList).name}
}
Register-ArgumentCompleter -CommandName Uninstall-PWSHModule -ParameterName ListName -ScriptBlock $scriptBlock

$scriptblock2 = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    if (($PSDefaultParameterValues.Keys -like '*GitHubUserID*')) {
    (Show-PWSHModule -ListName * -ErrorAction SilentlyContinue).name | Sort-Object -Unique
    }
}
Register-ArgumentCompleter -CommandName Uninstall-PWSHModule -ParameterName ModuleName -ScriptBlock $scriptBlock2
 
Export-ModuleMember -Function Uninstall-PWSHModule
#endregion
 
#endregion