I recently found myself on a long bus ride with my laptop and no Internet connection. I decided to create a PowerShell puzzle for myself to keep me busy until I reached my destination. It ended up being kind of interesting so I wanted to share my puzzle and the solution with here.
Challenge
PowerShell has the concept of “Get-*” cmdlets where these will only retrieve information but will never change anything. This makes Get commands safe to run, even if you don’t know what they do. PowerShell ships with hundreds of Get cmdlets. It would be great to know what kind of information each one provided. To do that I’d need to run every Get command. That’s simple enough but is complicated by the fact that most Get- cmdlets come with multiple ‘parameter sets’ and many are configured with mandatory parameters. This means that if we blindly run each command we’ll end up with a bunch of errors and prompts for user input.
So what we need to do is figure out which of the Get- commands can be run without passing any parameters. We then also want to exclude commands that run properly but don’t return any data by default.
Solution
Check out the script below. It will scan the system for every Get- command and then will figure out which ones can be executed directly without parameters and will actually return data. It then runs all of those and returns the first 5 objects so you can get a feel for what kind of data is returned. It will then display a report showing how many cmdlets met each criteria. On my system for example we can see that 143 commands can be executed and return something useful just by typing the name of the command and hitting enter.
When each command runs, it returns the first 5 results. As an example, here is a screenshot of what the script looks like in action:
# Gets a list of every Get cmdlet on the system and determine which commands can be run without providing paramaters and executes all those commands # Home; PowerShell; Default; Parameter Sets; # Some Commands such as Get-DSCResource return progress bars that stay on screen for the duration of execution. This surpresses them from being displayed $ProgressPreference=’SilentlyContinue’ # If you would like to review the output of each command as it executes, change this to $True and you'll need to press enter after each command finishes running $PauseAfterEach = $false # Stores details on how many commands were found, could be executed, etc $CommandReport = @() # List of commands that require parameters to execute $InvalidCommands = @() # List of commands that do not require parameters to execute AND return some data when executed without parameters $ValidCommands = @() # List of commands that do not require parameters but return nothing when executed without them $NoDataCommands = @() # Exclude Aliases as the source commands will also be included in the results $Commands = (((get-command get-* | where { $_.commandtype -ne 'Alias' }))).name # Add the first line to our report which includes every command avaialble in an array (including the list of actual commands for later analysis) $CommandReport += @{'ID'='1'; 'Description'= 'All Get-* cmdlets on the system excluding aliases'; Count=($Commands | Measure-Object).count; 'Commands' = $Commands} # Put any commands here that should display data without parameters but have some edge case issue that prevents them from working #Get-PSSessionConfiguration - Prompts if the WSMAN Service is not started. $ExcludeCommands = @' Get-PSSessionConfiguration '@ -split "`r`n" $Commands = $Commands | Where { $ExcludeCommands -notcontains $_ } $CommandReport += @{'ID'='2'; 'Description'= 'All manually excluded cmdlets'; Count=($ExcludeCommands | Measure-Object).count; 'Commands' = $ExcludeCommands} ForEach($Command in $Commands) { # Clear these variables after each iteration of the loop as they are used to control the flow $MandatoryParameters = $null $Output = $null # How many unique parameter sets this command offers $ParameterSetCount = ((Get-command $Command).ParameterSets | Measure-Object).count # If a default parameter set is present, it will be included here. Otherwise this will be $null $DefaultParameterSet = ((Get-command $Command).DefaultParameterSet) # If there is only one parameter set (which means PowerShell will execute it automatically) extract a list of the mandatory toggle for each parameter for this set if ($ParameterSetCount -eq 1) { $MandatoryParameters = (((get-command $Command).parametersets).parameters.ismandatory) } # If there is more than one parameter set but a default is configured, extract a list of the mandatory toggle for each parameter for the default if ($ParameterSetCount -gt 1 -and $DefaultParameterSet) { $MandatoryParameters = ((get-command $Command).ParameterSets | where { $_.isdefault -eq $true }).parameters.ismandatory } # If the single parameter set or default parameter set contain any values set to true for ismandatory, this means it will prompt the user upon execution. Exclude these # Alternatively if for any reason the mandatoryparameters variable is null, assume this command cannot be run without parameters if($MandatoryParameters -contains $True -or (!($MandatoryParameters))) { $InvalidCommands += $Command } else { # If $MandatoryParameters is $False it means this command can be run without parameters so try executing it and write any errors to the screen in Yellow # Select only the first 5 lines so that commands that return very large amounts of data do not fill the screen. 5 is enough to get a feel for what the command returns try { $Output = Invoke-Expression "$Command -ErrorAction Stop | Select -first 5" } catch { write-host "*** [$Command] - $_" -ForegroundColor Yellow } # Must typecase $Output as a string as some cmdlets return $false by default which is a valid response but would make this if condition otherwise fail if([string]$Output) { Write-Host "$Command" -ForegroundColor Green $Output | Out-string if($PauseAfterEach) { Read-Host "Press Enter for next Cmdlet" } $ValidCommands += $Command } else { $NoDataCommands += $Command } } } $CommandReport += @{'ID'='3'; 'Description'= 'All commands that returned valid output'; 'Count' = ($ValidCommands | Measure-Object).count; 'Commands' = $ValidCommands} $CommandReport += @{'ID'='4'; 'Description'= 'All commands that executed but did not return any data'; 'Count' = ($NoDataCommands | Measure-Object).count; 'Commands' = $NoDataCommands} $CommandReport += @{'ID'='5'; 'Description'= 'All commands that cannot execute without parameters'; 'Count' = ($InvalidCommands | Measure-Object).count; 'Commands' = $InvalidCommands} $CommandReport = $CommandReport | % { new-object PSObject -Property $_} $CommandReport | select ID, Description, Count, Commands | Format-Table -AutoSize