PSGalleryExplorer.psm1
# This is a locally sourced Imports file for local development. # It can be imported by the psm1 in local development to add script level variables. # It will merged in the build process. This is for local development only. #region scriptvariables function Get-DataLocation { $folderName = "PSGalleryExplorer" if ($PROFILE) { $script:dataPath = (Join-Path (Split-Path -Parent $PROFILE) $folderName) } else { $script:dataPath = "~\${$folderName}" } } $script:corps = @( 'AWS' 'Amazon' 'Amazon.com, Inc' 'AtlassianPS' 'Bentley Systems, Incorporated' 'BitTitan' 'BitTitan, Inc.' '(c) 2014 Microsoft Corporation. All rights reserved.' 'CData Software, Inc.' 'Chocolatey Software' 'Cisco Systems' 'Cisco' 'DSC Community' 'Dell Inc.' 'Dell' 'DevScope' 'Docker Inc.' 'Docker' 'Google Inc' 'Google Inc.' 'Hewlett Packard Enterprise Co.' 'Hewlett Packard Enterprise' 'Hewlett-Packard Enterprise' 'https://github.com/ebekker/ACMESharp' 'Ironman Software, LLC' 'JumpCloud' 'Microsoft (Xbox)' 'Microsoft Corp' 'Microsoft Corporation' 'Microsoft' 'Mozilla Corporation' 'Octopus Deploy Pty. Ltd' 'Octopus Deploy' 'Pentia A/S' 'Pentia' 'PowerShell.org' 'Pure Storage, Inc.' 'Red Gate Software Ltd.' 'SolarWinds Worldwide, LLC.' 'SolarWinds' 'SynEdgy Limited' 'Synergex International Corporation' 'VMware' 'VMware, Inc.' 'waldo.be' ) $script:regulars = @( 'BuildHelpers' 'Carbon' 'ImportExcel' 'Invokebuild' 'PendingReboot' 'PSDepend' 'PSDeploy' 'PSKoans' 'PSLogging' 'PSSlack' 'PSWindowsUpdate' 'Pester' 'PoshBot' 'Selenium' 'Write-ObjectToSQL' 'dbatools' 'oh-my-posh' 'posh-git' 'powershell-yaml' 'psake' ) $domain = 'amazonaws.com' $prefix = 'psge-pubxml' $target = 's3-accelerate' Get-DataLocation $script:dataFileZip = 'PSGalleryExplorer.zip' $script:dataFile = 'PSGalleryExplorer.xml' $script:dlURI = "$prefix.$target.$domain" $script:glData = $null #endregion <# .SYNOPSIS Confirm data output location. Creates output dir if not present. .DESCRIPTION Evaluates presence of data output location for xml dataset. If the directory is not found, it will be created. .EXAMPLE Confirm-DataLocation Confirms presence of data output location. Creates if not found. .OUTPUTS System.Boolean .NOTES None .COMPONENT PSGalleryExplorer #> function Confirm-DataLocation { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Verifying data set output location...' try { $pathEval = Test-Path -Path $script:dataPath -ErrorAction Stop }#try catch { $result = $false Write-Error $_ return $result }#catch if (-not ($pathEval)) { Write-Verbose -Message 'Creating output directory...' try { $newItemSplat = @{ ItemType = 'Directory' Path = $script:dataPath ErrorAction = 'Stop' } $null = New-Item @newItemSplat Write-Verbose -Message 'Created.' } catch { $result = $false Write-Error $_ return $result } }#if_TestPath else { Write-Verbose 'Data path confirmed.' } return $result }#Confirm-DataLocation <# .SYNOPSIS Confirms the XML dataset file is available and not beyond the expiration time. .DESCRIPTION Confirms the XML dataset file is present on the file system for use. Determines the age of the XML dataset file. Returns true if present and 9 days older or more. .EXAMPLE Confirm-XMLDataSet Checks for XML dataset and determines if it is 9 days older or more. .OUTPUTS System.Boolean .NOTES Used for PSGalleryExplorer .COMPONENT PSGalleryExplorer #> function Confirm-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best $dataFile = "$script:dataPath\$script:dataFile" Write-Verbose -Message 'Confirming valid and current data set...' try { $pathEval = Test-Path -Path $dataFile -ErrorAction Stop }#try catch { $result = $false Write-Error $_ return $result }#catch if (-not ($pathEval)) { $result = $false }#if_TestPath else { Write-Verbose 'Data file found. Checking date of file...' try { $fileData = Get-ChildItem -Path $dataFile -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } if ($fileData) { $creationDate = $fileData.CreationTime $now = Get-Date if (($now - $creationDate).Days -ge 9) { Write-Verbose 'Data file requires refresh.' $result = $false } else { Write-Verbose 'Data file verified' } } else { Write-Warning 'Unable to retrieve file information for PSGalleryExplorer data set.' $result = $false return $result } } return $result }#Confirm-XMLDataSet <# .SYNOPSIS Unzips the XML data set. .DESCRIPTION Evalutes for previous version of XML data set and removes if required. Expands the XML data set for use. .EXAMPLE Expand-XMLDataSet Unzips and expands the XML data set. .OUTPUTS System.Boolean .NOTES None .COMPONENT PSGalleryExplorer #> function Expand-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Testing if data set file alread exists...' try { $pathEval = Test-Path -Path "$script:dataPath\$script:dataFile" -ErrorAction Stop Write-Verbose -Message "EVAL: $true" }#try catch { $result = $false Write-Error $_ return $result }#catch if ($pathEval) { Write-Verbose -Message 'Removing existing data set file...' try { $removeItemSplat = @{ Force = $true Path = "$script:dataPath\$script:dataFile" ErrorAction = 'Stop' } Remove-Item @removeItemSplat }#try catch { $result = $false Write-Error $_ return $result }#catch }#if_pathEval Write-Verbose -Message 'Expanding data set archive...' try { $expandArchiveSplat = @{ DestinationPath = "$script:dataPath" Force = $true ErrorAction = 'Stop' Path = "$script:dataPath\$script:dataFileZip" } $null = Expand-Archive @expandArchiveSplat Write-Verbose -Message 'Expand completed.' }#try catch { $result = $false Write-Error $_ }#catch return $result }#Expand-XMLDataSet <# .SYNOPSIS Downloads XML Data set to device. .DESCRIPTION Retrieves XML Data zip file from web and downloads to device. .EXAMPLE Get-XMLDataSet Downloads XML data set to data path. .OUTPUTS System.Boolean .NOTES Overwrites existing zip file. .COMPONENT PSGalleryExplorer #> function Get-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Downloading XML data set...' try { $invokeWebRequestSplat = @{ OutFile = "$script:dataPath\$script:dataFileZip" Uri = "$script:dlURI/$script:dataFileZip" ErrorAction = 'Stop' } $oldProgressPreference = $progressPreference $progressPreference = 'SilentlyContinue' if ($PSEdition -eq 'Desktop') { $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru -UseBasicParsing } else { $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru } }#try catch { $result = $false Write-Error $_ }#catch finally { $progressPreference = $oldProgressPreference }#finally return $result }#Get-XMLDataSet <# .SYNOPSIS Evaluates if XML data set is in memory and kicks of child processes to obtain XML data set. .DESCRIPTION XML data set will be evaluated if already in memory. If not, a series of processes will be kicked off to load the XML data set for use. .EXAMPLE Import-XMLDataSet Loads the XML data set into memory. .OUTPUTS System.Boolean .NOTES Parent process for getting XML data. .COMPONENT PSGalleryExplorer #> function Import-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Verifying current state of XML data set...' if ($null -eq $script:glData) { $dataCheck = Invoke-XMLDataCheck if ($dataCheck) { try { $getContentSplat = @{ Path = "$script:dataPath\$script:dataFile" Raw = $true ErrorAction = 'Stop' } $fileData = Get-Content @getContentSplat $script:glData = $fileData | ConvertFrom-Clixml -ErrorAction Stop }#try catch { $result = $false Write-Error $_ }#catch }#if_dataCheck else { $result = $false }#else_dataCheck }#if_gldata return $result }#Import-XMLDataSet <# .SYNOPSIS Invokes all child functions required to process retrieving the XML data set file. .DESCRIPTION Runs all required child functions to successfull retrieve and process the XML data set file. .EXAMPLE Invoke-XMLDataCheck Downloads, expands, and verified the XML data set file. .OUTPUTS System.Boolean .NOTES Confirm-XMLDataSet Get-XMLDataSet Expand-XMLDataSet .COMPONENT PSGalleryExplorer #> function Invoke-XMLDataCheck { [CmdletBinding(ConfirmImpact = 'Low', SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false, HelpMessage = 'Skip confirmation')] [switch]$Force ) Begin { if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } if (-not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') } if (-not $PSBoundParameters.ContainsKey('WhatIf')) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') } Write-Verbose -Message ('[{0}] Confirm={1} ConfirmPreference={2} WhatIf={3} WhatIfPreference={4}' -f $MyInvocation.MyCommand, $Confirm, $ConfirmPreference, $WhatIf, $WhatIfPreference) $results = $true #assume the best }#begin Process { # -Confirm --> $ConfirmPreference = 'Low' # ShouldProcess intercepts WhatIf* --> no need to pass it on if ($Force -or $PSCmdlet.ShouldProcess("ShouldProcess?")) { Write-Verbose -Message ('[{0}] Reached command' -f $MyInvocation.MyCommand) $ConfirmPreference = 'None' $dataOutputDir = Confirm-DataLocation if ($dataOutputDir -eq $true) { $confirm = Confirm-XMLDataSet if (-not $Confirm -eq $true) { $retrieve = Get-XMLDataSet if ($retrieve -eq $true) { $expand = Expand-XMLDataSet if (-not $expand -eq $true) { $results = $false } } else { $results = $false } }#if_Confirm }#if_dataoutput else { $results = $false }#else_dataoutput }#if_Should }#process End { return $results }#end }#Invoke-XMLDataCheck <# .EXTERNALHELP PSGalleryExplorer-help.xml #> function Find-PSGModule { [CmdletBinding()] param ( [Parameter(ParameterSetName = 'GalleryDownloads', HelpMessage = 'Find by PowerShell Gallery Downloads')] [switch] $ByDownloads, [Parameter(ParameterSetName = 'GitHub', HelpMessage = 'Find by GitHub metrics')] [ValidateSet( 'StarCount', 'Subscribers', 'Watchers', 'Forks' )] [string] $ByGitHubInfo, [Parameter(ParameterSetName = 'Update', HelpMessage = 'Find by recently updated')] [string] [ValidateSet( 'GalleryUpdate', 'GitUpdate' )] $ByRecentUpdate, [Parameter(ParameterSetName = 'GalleryDownloads', HelpMessage = 'Find random PowerShell Gallery modules')] [switch] $ByRandom, [Parameter(ParameterSetName = 'Names', HelpMessage = 'Find by name')] [string] [ValidatePattern('^[-_.A-Za-z0-9]+')] $ByName, [Parameter(ParameterSetName = 'Tags', HelpMessage = 'Find by tag')] [string] [ValidatePattern('^[A-Za-z]+')] $ByTag, [Parameter(HelpMessage = 'Include corporation results')] [switch] $IncludeCorps, [Parameter(HelpMessage = 'Include more popular results')] [switch] $IncludeRegulars, [Parameter(Mandatory = $false, HelpMessage = 'Max number of modules to return')] [int] $NumberToReturn = 35 ) Write-Verbose -Message 'Verifying XML Data Set Availability...' if (Import-XMLDataSet) { Write-Verbose -Message 'Verified.' #__________________________________________________________ Write-Verbose -Message 'Processing exclusions...' if ($IncludeCorps -and $IncludeRegulars) { $dataSet = $script:glData } elseif ($IncludeCorps) { $dataSet = $script:glData | Where-Object { $_.Name -notin $script:regulars } } elseif ($IncludeRegulars) { $dataSet = $script:glData | Where-Object { $_.AdditionalMetadata.CompanyName -notin $script:corps -and $_.Author -notin $script:corps -and $_.AdditionalMetadata.copyright -notin $script:corps } } else { $dataSet = $script:glData | Where-Object { $_.AdditionalMetadata.CompanyName -notin $script:corps -and $_.Author -notin $script:corps -and $_.AdditionalMetadata.copyright -notin $script:corps -and $_.Name -notin $script:regulars } } Write-Verbose -Message 'Exclusions completed.' #__________________________________________________________ if ($ByGitHubInfo) { Write-Verbose -Message 'ByGitHubInfo' $gitModules = $dataSet | Where-Object { $_.GitHubInfo.GitStatus -eq $true } $find = $gitModules | Sort-Object -Property { [int]$_.GitHubInfo.$ByGitHubInfo } -Descending | Select-Object -First $NumberToReturn }#if_ByGitHubInfo elseif ($ByDownloads) { Write-Verbose -Message 'ByDownloads' $find = $dataSet | Sort-Object -Property { [int]$_.AdditionalMetadata.downloadCount } -Descending | Select-Object -First $NumberToReturn }#elseif_ByDownloads elseif ($ByRecentUpdate) { Write-Verbose -Message 'ByRecentUpdate' switch ($ByRecentUpdate) { 'GalleryUpdate' { $find = $dataSet | Sort-Object -Property { [datetime]$_.AdditionalMetadata.updated } -Descending | Select-Object -First $NumberToReturn } 'GitUpdate' { $gitModules = $dataSet | Where-Object { $_.GitHubInfo.GitStatus -eq $true } $find = $gitModules | Sort-Object -Property { [datetime]$_.GitHubInfo.Updated } -Descending | Select-Object -First $NumberToReturn } } }#elseif_ByRecentUpdate elseif ($ByRandom) { Write-Verbose -Message 'ByRandom' $captured = @() $randoms = @() for ($i = 0; $i -lt $NumberToReturn; $i++) { $thisRound = $null $thisRound = $dataSet | Where-Object { $captured -notcontains $_.Name } | Get-Random $captured += $thisRound.Name $randoms += $thisRound } $find = $randoms | Sort-Object -Property { [int]$_.AdditionalMetadata.downloadCount } -Descending }#elseif_ByRandom elseif ($ByName) { Write-Verbose -Message 'ByName' $find = $dataSet | Where-Object { $_.Name -eq $ByName } }#ByName elseif ($ByTag) { Write-Verbose -Message 'ByTag' $tagModules = $dataSet | Where-Object { $ByTag -in $_.Tags } $find = $tagModules | Sort-Object -Property { [int]$_.AdditionalMetadata.downloadCount } -Descending | Select-Object -First $NumberToReturn }#ByTag #__________________________________________________________ }#if_Import-XMLDataSet else { Write-Warning -Message 'PSGalleryExplorer was unable to source the required data set file.' Write-Warning -Message 'Ensure you have an active internet connection' return }#else_Import-XMLDataSet Write-Verbose -Message 'Adding output properties to objects...' foreach ($item in $find) { $metrics = $null $metrics = @{ Downloads = $item.AdditionalMetadata.downloadCount LastUpdate = $item.AdditionalMetadata.lastUpdated GitStar = $item.GitHubInfo.StarCount GitSub = $item.GitHubInfo.Subscribers GitWatch = $item.GitHubInfo.Watchers GitFork = $item.GitHubInfo.Forks GitUpdate = $item.GitHubInfo.Updated } $item | Add-Member -NotePropertyMembers $metrics -TypeName Asset -Force $item.PSObject.TypeNames.Insert(0, 'PSGEFormat') }#foreach_find Write-Verbose -Message 'Properties addition completed.' return $find }#Find-PSGModule |