pwshCloudCommands.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 script variables # $script:resourcePath = "$PSScriptRoot\Resources" function Get-DataLocation { $folderName = 'pwshCloudCommands' if ($PROFILE) { $script:dataPath = (Join-Path (Split-Path -Parent $PROFILE) $folderName) } else { $script:dataPath = "~\${$folderName}" } } $domain = 'cloudfront.net' $target = 'd42gqkczylm43' Get-DataLocation $script:dataFolderZip = 'pwshcloudcommands.zip' $script:dataFolder = 'pwshcloudcommandsXML' $script:dlURI = '{0}.{1}' -f $target, $domain <# .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 Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> 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 } catch { $result = $false Write-Error $_ return $result } 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.' } #else_TestPath 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 Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Confirm-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best $dataFile = '{0}/{1}' -f $script:dataPath, $script:dataFolderZip $dataExtract = '{0}/{1}' -f $script:dataPath, $script:dataFolder Write-Verbose -Message 'Confirming valid and current data set...' try { $pathEval = Test-Path -Path $dataFile -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } if (-not ($pathEval)) { $result = $false } #if_pathEval 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.LastWriteTime $now = Get-Date if (($now - $creationDate).Days -ge 9) { Write-Verbose 'Data file requires refresh.' $result = $false } else { Write-Verbose 'Data file verified' Write-Verbose 'Verifying extracted data set...' $dataSet = $null $dataSet = Get-ChildItem -Path $dataExtract -ErrorAction SilentlyContinue if ($null -eq $dataSet) { Write-Verbose 'Extracted data set is missing.' $result = $false return $result } } } #if_fileData else { Write-Warning 'Unable to retrieve file information for pwshCloudCommands data set.' $result = $false return $result } #else_fileData } #else_pathEval return $result } #Confirm-XMLDataSet <# .SYNOPSIS Unzips the XML data set. .DESCRIPTION Evaluates 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 Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Expand-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best $dataFolder = '{0}/{1}' -f $script:dataPath, $script:dataFolder Write-Verbose -Message 'Testing if data set folder already exists...' try { $pathEval = Test-Path -Path $dataFolder -ErrorAction Stop Write-Verbose -Message "EVAL: $true" } catch { $result = $false Write-Error $_ return $result } if ($pathEval) { Write-Verbose -Message 'Removing existing data set folder...' try { $removeItemSplat = @{ Force = $true Path = $dataFolder Recurse = $true 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 = '{0}/{1}' -f $script:dataPath, $script:dataFolder Force = $true ErrorAction = 'Stop' Path = '{0}/{1}' -f $script:dataPath, $script:dataFolderZip } $null = Expand-Archive @expandArchiveSplat Write-Verbose -Message 'Expand completed.' } #try catch { $result = $false Write-Error $_ } #catch return $result } #Expand-XMLDataSet <# .SYNOPSIS Processes discovered cloud commands and returns formatted results in a PSCustomObject format. .DESCRIPTION This function is called to process discovered cloud commands found in files. The function is passed a PSCustomObject that contains the discovered cloud command. Findings are organized by module name, function, and file found in. The function returns a PSCustomObject that contains the formatted results. .EXAMPLE Format-FileFinding -CloudCommandObj $cloudCommands -FileInfo $fileInfo .PARAMETER CloudCommandObj Object that contains the discovered cloud command(s). .PARAMETER FileInfo Object containing the file information that cloud command discovery was done on. .OUTPUTS System.Management.Automation.PSCustomObject .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ ModuleName Name ---------- ---- AWS.Tools.SimpleSystemsManagement Get-SSMDocumentList AWS.Tools.SimpleSystemsManagement Get-SSMDocumentList AWS.Tools.EC2 Start-EC2Instance AWS.Tools.S3 Get-S3Bucket Az.Accounts Connect-AzAccount Az.Accounts Set-AzContext Az.Resources Get-AzResourceGroup .COMPONENT pwshCloudCommands #> function Format-FileFinding { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'Object that contains the discovered cloud commands')] [psobject] $CloudCommandObj, [Parameter(Mandatory = $true, HelpMessage = 'Object containing the file information that cloud command discovery was done on')] [psobject] $FileInfo ) $results = [System.Collections.ArrayList]::new() $uniqueModuleNames = $CloudCommandObj | Select-Object -ExpandProperty ModuleName -Unique foreach ($module in $uniqueModuleNames) { $uniqueFunctions = $cloudCommands | Where-Object { $_.ModuleName -eq $module } | Select-Object -ExpandProperty Name -Unique $obj = [PSCustomObject]@{ ModuleName = $module Functions = $uniqueFunctions FileName = $FileInfo.Name FilePath = $FileInfo.FullName } [void]$results.Add($obj) } #foreach_module return $results } #Format-FileFinding <# .SYNOPSIS Retrieves all PowerShell files from specified path .DESCRIPTION Retrieves all Get-ChildItem information with a ps1 filter from the specified path. .EXAMPLE Get-AllPowerShellFile -Path "$env:HOME\pathToEvaluate" Retrieves all PowerShell files from specified path .PARAMETER Path Path to search for PowerShell files .OUTPUTS System.IO.FileInfo .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Get-AllPowerShellFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Path to search for PowerShell files')] [string]$Path ) Write-Verbose -Message ('Retrieving PowerShell files from {0} ' -f $Path) $getChildItemSplat = @{ Path = $Path Filter = '*.ps1' Recurse = $true Force = $true ErrorAction = 'SilentlyContinue' } $psFiles = Get-ChildItem @getChildItemSplat return $psFiles } #Get-AllPowerShellFile <# .SYNOPSIS Initiates FunctionQuery against XML dataset for each discovered PowerShell function token. .DESCRIPTION Takes in a collection of discovered tokens from file. Parses tokens for PowerShell function names. Initiates FunctionQuery against XML dataset for each discovered PowerShell function token. Returns search results. .EXAMPLE Get-CloudCommandFromToken -Tokens $tokens Initiates FunctionQuery search for each discovered PowerShell function token. .PARAMETER Tokens A collection of discovered tokens from file. .OUTPUTS Deserialized.System.Management.Automation.PSCustomObject .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Get-CloudCommandFromToken { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'A collection of discovered tokens from file')] [psobject] $Tokens ) $results = [System.Collections.ArrayList]::new() foreach ($token in $Tokens) { if ($token.Type -ne 'Command' -or $token.Content -notlike '*-*') { continue } # ----------------------- # resets $search = $null # ----------------------- $search = Search-XMLDataSet -FunctionQuery $token.Content if ($null -ne $search) { [void]$results.Add($search) } } #foreach_token return $results } #Get-CloudCommandFromToken <# .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 Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ Overwrites existing zip file. .COMPONENT pwshCloudCommands #> function Get-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Downloading XML data set...' try { $invokeWebRequestSplat = @{ OutFile = '{0}/{1}' -f $script:dataPath, $script:dataFolderZip Uri = 'https://{0}/{1}' -f $script:dlURI, $script:dataFolderZip 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 Invokes all child functions required to process retrieving the XML data set file. .DESCRIPTION Runs all required child functions to successfully retrieve and process the XML data set file. .EXAMPLE Invoke-XMLDataCheck Downloads, expands, and verified the XML data set file. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ Confirm-XMLDataSet Get-XMLDataSet Expand-XMLDataSet .COMPONENT pwshCloudCommands #> 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 (($Confirm -eq $false)) { $retrieve = Get-XMLDataSet if ($retrieve -eq $true) { $expand = Expand-XMLDataSet if ($expand -eq $false) { $results = $false } } else { $results = $false } } #if_Confirm } #if_data_output else { $results = $false } #else_data_output } #if_Should } #process End { return $results } #end } #Invoke-XMLDataCheck <# .SYNOPSIS Takes in free form query and optimizes for use in a verb / term bases search. .DESCRIPTION Takes in free form query and removes all stop words, special characters, and white space. Identifies verb and terms and forms an object to be used in a search. .EXAMPLE Optimize-Input -SearchInput 'write an object to a s3 bucket' Takes in free form query and optimizes for use in a verb / term bases search. .OUTPUTS System.Management.Automation.PSCustomObject .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Optimize-Input { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'TBD')] [string[]]$SearchInput ) # $SearchInput = 'write write *OBJECT* to bucket $&*@(*@' $splitInput = $SearchInput.Split(' ') $noSpecialCharInput = $splitInput -replace '\W', '' $lowerInput = $noSpecialCharInput.ToLower() $noDupInput = $lowerInput | Select-Object -Unique $noEmptyArray = $noDupInput.Split('', [System.StringSplitOptions]::RemoveEmptyEntries) if ([string]::IsNullOrWhiteSpace($noEmptyArray)) { return $null } $cleanInput = Remove-StopWord -SearchInput $noEmptyArray $verbs = [System.Collections.ArrayList]::new() $terms = [System.Collections.ArrayList]::new() $psVerbs = Get-Verb | Select-Object -ExpandProperty Verb $cleanInput | ForEach-Object { if ($psVerbs -contains $_) { [void]$verbs.Add($_) } else { [void]$terms.Add($_) } } $obj = [PSCustomObject]@{ Verbs = $verbs Terms = $terms } return $obj } #Optimize-Input <# .SYNOPSIS Parses the given file and returns a list of all the command and command arguments tokens. .DESCRIPTION Gets contents of specified file and parses it into a list of tokens. Command and command arguments tokens are returned. .EXAMPLE Read-TokenCommandsFromFile -FilePath $file.FullName Returns all the command and command arguments tokens in the specified file. .PARAMETER FilePath Path to file .OUTPUTS System.Management.Automation.PSToken .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Read-TokenCommandsFromFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'Path to file')] [string] $FilePath ) if (-not (Test-Path -Path $FilePath -PathType Leaf)) { Write-Warning -Message ('{0} is not a valid file path.' -f $FilePath) throw $_ } $getContentSplat = @{ Path = $FilePath Raw = $true ErrorAction = 'Stop' } try { $rawFileContent = Get-Content @getContentSplat } catch { Write-Warning -Message ('Contents of {0} could not be retrieved' -f $FilePath) throw $_ } if ([string]::IsNullOrWhiteSpace($rawFileContent)) { Write-Warning -Message ('Contents of {0} is empty' -f $FilePath) return } $commandTokens = [System.Collections.ArrayList]::new() $tokens = [System.Management.Automation.PSParser]::Tokenize(($rawFileContent), [ref]$null) foreach ($token in $tokens) { if ($token.Type -ne 'Command' -and $token.Type -ne 'CommandArgument') { continue } [void]$commandTokens.Add($token) } #foreach_token return $commandTokens } #Read-TokenCommandsFromFile <# .SYNOPSIS Removes stop words from provided input text. .DESCRIPTION Parses provided input text and removes stop words. .EXAMPLE Remove-StopWord -SearchInput $searchInput Returns the input text with stop words removed. .OUTPUTS System.String .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshCloudCommands #> function Remove-StopWord { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'TBD')] [string[]]$SearchInput ) $stopWords = @( 'a' 'about' 'above' 'actually' 'after' 'again' 'against' 'all' 'almost' 'also' 'although' 'always' 'am' 'an' 'and' 'any' 'are' 'as' 'at' 'be' 'became' 'because' 'become' 'been' 'before' 'being' 'below' 'between' 'both' 'but' 'by' 'can' 'cannot' 'could' 'did' 'do' 'does' 'doing' 'down' 'during' 'each' 'either' 'else' 'few' 'for' 'from' 'further' 'had' 'has' 'have' 'having' 'he' 'hence' 'her' 'here' 'hers' 'herself' 'him' 'himself' 'his' 'how' 'i' 'if' 'in' 'into' 'is' 'it' 'its' 'itself' 'just' 'may' 'maybe' 'me' 'might' 'mine' 'more' 'most' 'must' 'my' 'myself' 'neither' 'no' 'nor' 'not' 'of' 'off' 'oh' 'ok' 'on' 'once' 'only' 'or' 'other' 'ought' 'our' 'ours' 'ourselves' 'out' 'over' 'own' 'same' 'she' 'so' 'some' 'such' 'than' 'that' 'the' 'their' 'theirs' 'them' 'themselves' 'then' 'there' 'these' 'they' 'this' 'those' 'through' 'to' 'too' 'under' 'very' 'was' 'we' 'were' 'what' 'whenever' 'whereas' 'wherever' 'whether' 'who' 'whoever' 'whom' 'whose' 'why' 'will' 'with' 'within' 'without' 'would' 'yes' 'yet' 'you' 'your' 'yours' 'yourself' 'yourselves' ) $cleanInput = $SearchInput | Where-Object { $stopWords -notcontains $_ } return $cleanInput } #Remove-StopWord <# .SYNOPSIS Queries XML data set to discover PowerShell cloud commands based on provided inputs. .DESCRIPTION Primary private function that performs the appropriate query against the XML data set. .EXAMPLE Search-XMLDataSet -Query $cleanQuery Performs a general search against the XML data set. .EXAMPLE Search-XMLDataSet -FunctionQuery 'Write-S3Object' Performs a function query against the XML data set. .EXAMPLE Search-XMLDataSet -WildCardQuery 'write-s3*' Performs a wildcard query against the XML data set. .PARAMETER Query Clean input query in Verb Term Format. .PARAMETER FunctionQuery PowerShell function name to query against the XML data set. .PARAMETER WildCardQuery PowerShell function name with wildcard to query against the XML data set. .PARAMETER AllInfo Return all info from the XML data set. .PARAMETER Filter Cloud filter to apply to the query. This drastically improves the performance of the query. .OUTPUTS Deserialized.System.Management.Automation.PSCustomObject .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ If using a generic query this function expects a clean query input that is currently being provided by Optimize-Input .COMPONENT pwshCloudCommands #> function Search-XMLDataSet { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Clean input query in Verb Term Format', ParameterSetName = 'Input')] [psobject]$Query, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'PowerShell function name to query against the XML data set', ParameterSetName = 'Function')] [string]$FunctionQuery, [Parameter(Mandatory = $true, Position = 2, HelpMessage = 'PowerShell function name with wildcard to query against the XML data set', ParameterSetName = 'WildCard')] [string]$WildCardQuery, [Parameter(Mandatory = $true, Position = 3, HelpMessage = 'Return all info from the XML data set', ParameterSetName = 'All')] [switch]$AllInfo, [Parameter(Mandatory = $false, Position = 4, HelpMessage = 'Cloud filter to apply to the query')] [ValidateSet('AWS', 'Azure', 'Oracle')] [string] $Filter ) Write-Debug -Message ('Parameter set: {0}' -f $PSCmdlet.ParameterSetName) switch ($Filter) { 'AWS' { $searchFilter = 'AWS.Tools.*' } 'Azure' { $searchFilter = 'Az.*' } 'Oracle' { $searchFilter = 'OCI.*' } Default { $searchFilter = '*' } } #switch_filter Write-Debug -Message ('Search filter: {0}' -f $searchFilter) Write-Verbose -Message 'Retrieving xml file info...' $xmlDataPath = '{0}\{1}' -f $script:dataPath, $script:dataFolder $getChildItemSplat = @{ Path = $xmlDataPath Filter = $searchFilter ErrorAction = 'Stop' } try { $xmlDataFiles = Get-ChildItem @getChildItemSplat } catch { Write-Warning -Message 'An error was encountered getting xml file info.' Write-Error $_ throw } # special case for Azure selection where we will also retrieve Graph Modules if ($Filter -eq 'Azure') { Write-Debug -Message 'Retrieving Azure Graph xml file info...' $getChildItemSplat = @{ Path = $xmlDataPath Filter = 'Microsoft.Graph.*' ErrorAction = 'Stop' } try { $graphFiles = Get-ChildItem @getChildItemSplat } catch { Write-Warning -Message 'An error was encountered getting xml file info.' Write-Error $_ throw } $xmlDataFiles += $graphFiles } Write-Verbose -Message 'Running query...' if ($PSCmdlet.ParameterSetName -eq 'Function') { #------------------------------ $xmlCount = ($xmlDataFiles | Measure-Object).Count $i = 0 #------------------------------ foreach ($xml in $xmlDataFiles) { # ------------------------------ # resets $rawData = $null $xmlData = $null $function = $null # ------------------------------ $i++ Write-Progress -Activity 'Searching...' -PercentComplete ($i / $xmlCount * 100) Write-Debug -Message ('Processing {0}' -f $xml.Name) try { $rawData = Get-Content $xml.FullName -Raw -ErrorAction 'Stop' } catch { Write-Warning -Message ('An error was encountered reading xml data file {0}...' -f $xml.Name) Write-Error $_ throw } if ($rawData -match $FunctionQuery) { Write-Debug -Message ('Function query match found in {0}' -f $xml.Name) $xmlData = $rawData | ConvertFrom-Clixml } else { continue } if ($xmlData.ExportedCommands.Keys -contains $FunctionQuery) { $function = $xmlData.Functions | Where-Object { $_.Name -eq $FunctionQuery -or $_.DisplayName -eq $FunctionQuery } if ($null -ne $function) { if ($function.CommandType.Value -eq 'Alias') { Write-Debug -Message ('Alias located in: {0}' -f $xml.Name) Write-Warning -Message ('Aliases not supported - {0} is an alias for {1}' -f $function.DisplayName, $function.ResolvedCommand) continue } else { Write-Debug -Message ('Function command found: {0}' -f $function.Name) } Add-Member -InputObject $function -MemberType NoteProperty -Name 'ModuleName' -Value $xmlData.Name -Force $function.PSObject.TypeNames.Insert(0, 'pFindCCFormat') return $function } } } #foreach_xml return $null } #if_function_query elseif ($PSCmdlet.ParameterSetName -eq 'WildCard') { #------------------------------ $xmlCount = ($xmlDataFiles | Measure-Object).Count $i = 0 $matchResults = [System.Collections.ArrayList]::new() $words = $WildCardQuery.Split('*') $wordsArray = $words | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } #------------------------------ foreach ($xml in $xmlDataFiles) { # ------------------------------ # resets $rawData = $null $xmlData = $null $function = $null $fileParse = $false # ------------------------------ $i++ Write-Progress -Activity 'Searching...' -PercentComplete ($i / $xmlCount * 100) Write-Debug -Message ('Processing {0}' -f $xml.Name) try { $rawData = Get-Content $xml.FullName -Raw -ErrorAction 'Stop' } catch { Write-Warning -Message ('An error was encountered reading cloud data file {0}...' -f $xml.Name) Write-Error $_ throw } foreach ($word in $wordsArray) { if ($rawData -match $word) { Write-Debug -Message ('Match found in {0}' -f $xml.Name) $fileParse = $true } } if ($fileParse -eq $true) { $xmlData = $rawData | ConvertFrom-Clixml } else { continue } foreach ($function in $xmlData.Functions) { Write-Debug -Message ('....Processing {0}' -f $function.Name) if ($function.Name -like $WildCardQuery -or $function.DisplayName -like $WildCardQuery) { Write-Debug -Message ('Function query matched: {0}' -f $function.Name) if ($function.CommandType.Value -eq 'Alias') { Write-Debug -Message ('Alias located in: {0}' -f $xml.Name) Write-Warning -Message ('Aliases not supported - {0} is an alias for {1}' -f $function.DisplayName, $function.ResolvedCommand) continue } Add-Member -InputObject $function -MemberType NoteProperty -Name 'ModuleName' -Value $xmlData.Name -Force $function.PSObject.TypeNames.Insert(0, 'pFindCCFormat') [void]$matchResults.Add($function) } } #foreach_function } #foreach_xml return $matchResults } #elseif_wildcard elseif ($AllInfo -eq $true) { #------------------------------ $xmlCount = ($xmlDataFiles | Measure-Object).Count $i = 0 $matchResults = [System.Collections.ArrayList]::new() #------------------------------ foreach ($xml in $xmlDataFiles) { # ------------------------------ # resets $rawData = $null $xmlData = $null $function = $null $fileParse = $false # ------------------------------ $i++ Write-Progress -Activity 'Searching...' -PercentComplete ($i / $xmlCount * 100) Write-Debug -Message ('Processing {0}' -f $xml.Name) try { $rawData = Get-Content $xml.FullName -Raw -ErrorAction 'Stop' $xmlData = $rawData | ConvertFrom-Clixml } catch { Write-Warning -Message ('An error was encountered reading cloud data file {0}...' -f $xml.Name) Write-Error $_ throw } foreach ($function in $xmlData.Functions) { Write-Debug -Message ('....Processing {0}' -f $function.Name) if ($function.Name -like $WildCardQuery -or $function.DisplayName -like $WildCardQuery) { Write-Debug -Message ('Function query matched: {0}' -f $function.Name) if ($function.CommandType.Value -eq 'Alias') { # skip aliases continue } Add-Member -InputObject $function -MemberType NoteProperty -Name 'ModuleName' -Value $xmlData.Name -Force $function.PSObject.TypeNames.Insert(0, 'pFindCCFormat') [void]$matchResults.Add($function) } } #foreach_function } #foreach_xml return $matchResults } #elseif_all else { #------------------------------ $xmlCount = ($xmlDataFiles | Measure-Object).Count $i = 0 $matchResults = [System.Collections.ArrayList]::new() #------------------------------ foreach ($xml in $xmlDataFiles) { # --------------------------- # resets $rawData = $null $xmlData = $null $fileParse = $false # --------------------------- $i++ Write-Progress -Activity 'Searching...' -PercentComplete ($i / $xmlCount * 100) Write-Debug -Message ('Processing {0}' -f $xml.Name) try { $rawData = Get-Content $xml.FullName -Raw -ErrorAction 'Stop' } catch { Write-Warning -Message ('An error was encountered reading cloud data file {0}...' -f $xml.Name) Write-Error $_ throw } #-------------------------------- # raw string match checks foreach ($verb in $Query.Verbs) { if ($rawData -match $verb) { $fileParse = $true } } foreach ($term in $Query.Terms) { if ($rawData -match $term) { $fileParse = $true } } #-------------------------------- if ($fileParse -eq $true) { $xmlData = $rawData | ConvertFrom-Clixml } else { continue } foreach ($function in $xmlData.Functions) { # --------------------------- # resets $verbMatch = $false [int]$matchScore = 0 # --------------------------- foreach ($verb in $Query.Verbs) { if ($function.Verb -match $verb) { Write-Debug -Message ('{0} matched verb: {1}' -f $function.Name, $verb) $matchScore += 20 $verbMatch = $true } } #foreach_verb foreach ($term in $Query.Terms) { if ($function.Noun -match $term) { Write-Debug -Message ('{0} matched noun: {1}' -f $function.Name, $term) $matchScore += 10 } if ($function.Synopsis -match $term) { Write-Debug -Message ('{0} Synopsis matched: {1}' -f $function.Name, $term) $matchScore += ($function.Synopsis | Select-String $term -AllMatches).Matches.Value.count } if ($function.Description -match $term) { Write-Debug -Message ('{0} Description matched: {1}' -f $function.Name, $term) $matchScore += ($function.Description | Select-String $term -AllMatches).Matches.Value.count } } #foreach_term if ($verbMatch -eq $true ) { [int]$cutOff = 20 } else { [int]$cutOff = 0 } Write-Debug -Message ('Match score: {0}' -f $matchScore) Write-Debug -Message ('Cutoff: {0}' -f $matchScore) if ($matchScore -gt $cutOff) { Add-Member -InputObject $function -MemberType NoteProperty -Name 'MatchScore' -Value $matchScore -Force Add-Member -InputObject $function -MemberType NoteProperty -Name 'ModuleName' -Value $xmlData.Name -Force $function.PSObject.TypeNames.Insert(0, 'pFindCCFormat') [void]$matchResults.Add($function) } } #foreach_function } #foreach_xmldatafiles return $matchResults | Where-Object { $_.MatchScore -gt 0 } } #else_input } #Search-XMLDataSet <# .EXTERNALHELP pwshCloudCommands-help.xml #> function Find-CloudCommand { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Search input for PowerShell cloud commands.')] [ValidateLength(3, 60)] [string] $Query, [Parameter(Mandatory = $false, Position = 1, HelpMessage = 'Filters the search to a specific cloud platform')] [ValidateSet('AWS', 'Azure', 'Oracle')] [string] $Filter, [Parameter(Mandatory = $false, Position = 2, HelpMessage = 'Retrieves all search results without limiting the number')] [switch] $AllResults ) Write-Verbose -Message 'Verifying XML Data Set Availability...' $dataSet = Invoke-XMLDataCheck if ($dataSet -ne $true) { Write-Warning -Message 'pwshCloudCommands was unable to source the required data set files.' Write-Warning -Message 'Ensure you have an active internet connection' return } Write-Verbose 'Determining query type...' if ($Query -match '^(\w+-\w+)$') { Write-Verbose -Message 'Query is a valid function name.' $queryType = 'function' $searchXMLDataSetSplat = @{ FunctionQuery = $Query } } elseif ($Query -match '^\w.*\*.*$' -or $Query -match '^\*.*\w.*$') { Write-Verbose -Message 'Query is a wildcard function search.' $queryType = 'wildcard' $searchXMLDataSetSplat = @{ WildCardQuery = $Query } } else { Write-Verbose -Message 'Free form query. Optimizing query input' $queryType = 'freeform' $cleanInput = Optimize-Input -SearchInput $Query $searchXMLDataSetSplat = @{ Query = $cleanInput } } if ($Filter) { $searchXMLDataSetSplat.Add('Filter', $Filter) } Write-Verbose ($searchXMLDataSetSplat | Out-String) $results = Search-XMLDataSet @searchXMLDataSetSplat if ($queryType -eq 'freeform' -and $AllResults -eq $false) { $results = $results | Sort-Object MatchScore -Descending | Select-Object -First 30 } return $results | Sort-Object MatchScore -Descending } #Find-CloudCommand <# .EXTERNALHELP pwshCloudCommands-help.xml #> function Get-AllCloudCommandInfo { [CmdletBinding()] param ( [Parameter(Mandatory = $false, Position = 0, HelpMessage = 'Filters the search to a specific cloud platform')] [ValidateSet('AWS', 'Azure', 'Oracle')] [string] $Filter ) Write-Verbose -Message 'Verifying XML Data Set Availability...' $dataSet = Invoke-XMLDataCheck if ($dataSet -ne $true) { Write-Warning -Message 'pwshCloudCommands was unable to source the required data set files.' Write-Warning -Message 'Ensure you have an active internet connection' return } $searchXMLDataSetSplat = @{ AllInfo = $true } if ($Filter) { $searchXMLDataSetSplat.Add('Filter', $Filter) } Write-Verbose ($searchXMLDataSetSplat | Out-String) $results = Search-XMLDataSet @searchXMLDataSetSplat return $results } #Get-AllCloudCommandInfo <# .EXTERNALHELP pwshCloudCommands-help.xml #> function Get-CloudCommandFromFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'File or directory path to be evaluated for cloud command usage')] [string] $Path ) Write-Verbose -Message 'Verifying XML Data Set Availability...' $dataSet = Invoke-XMLDataCheck if ($dataSet -ne $true) { Write-Warning -Message 'pwshCloudCommands was unable to source the required data set files.' Write-Warning -Message 'Ensure you have an active internet connection' return } try { $target = Get-Item -Path $Path -ErrorAction 'Stop' } catch { Write-Error $_ throw } if ($target.PSIsContainer -eq $true) { $pwshFileInfo = Get-AllPowerShellFile -Path $Path if (-not ($pwshFileInfo)) { Write-Warning -Message ('No PowerShell files found at {0}' -f $Path) return } } else { try { $pwshFileInfo = Get-ChildItem -Path $Path -ErrorAction 'Stop' } catch { Write-Error $_ throw } if ($pwshFileInfo.Extension -ne '.ps1') { Write-Warning -Message ('{0} is not a PowerShell file.' -f $pwshFileInfo.Name) return } } $results = [System.Collections.ArrayList]::new() foreach ($file in $pwshFileInfo) { # ---------------------------- # resets $tokens = $null $cloudCommands = $null $fileResults = $null # ---------------------------- $tokens = Read-TokenCommandsFromFile -FilePath $file.FullName $cloudCommands = Get-CloudCommandFromToken -Tokens $tokens if ($cloudCommands) { $fileResults = Format-FileFinding -CloudCommandObj $cloudCommands -FileInfo $file $obj = New-Object PSObject -Property ([ordered]@{ FileName = $file.Name CloudCommands = $fileResults })#psobject [void]$results.Add($obj) } } #foreach_file return $results } #Get-CloudCommandFromFile |