I'm always looking for ways to help teach PowerShell and the other day I thought why not have PowerShell teach you itself? I have created a PowerShell script that dynamically generates a quiz on cmdlets and functions installed on your computer. In short the quiz question shows you a command synopsis and then presents a menu of possible answers. You select the answer. Given the verb-noun pattern of command names this *should* be easy, but you might be surprised.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Right now, the quiz comes in a stand-alone script. At some point I'll most likely add it to my PSTeachingTools module which you can install from the PowerShell Gallery. The script has help so after you download it you can get it:
help C:\scripts\PSquiz.ps1 -full
By default the script will find all cmdlets and functions from modules installed on your computer that have a properly defined synopsis. I've tried to include some filtering to handle buggy commands or those that have no help. You can also exclude one or more modules and wild cards are allowed. Or, you can specify a specific module. After the script builds a cache of commands it autogenerates a question and prompts you for an answer:
If you answer wrong, the script will display the correct answer. To continue with another question, press any key. The script will keep track of your progress and when you eventually quit, display a color coded summary result.
For now, you can find the script here.
#requires -version 5.0 #PSQuiz.ps1 <# .Synopsis Run a PowerShell quiz .Description Use this script to test your knowledge of PowerShell commands, which given the Verb-Noun naming pattern should be pretty easy. The default behavior is to use all cmdlets and functions from installed modules with an option to exclude an array of module names. Wildcards are allowed. You also have the option to specify a single module for testing. .Example PS C:\> c:\scripts\PSQuiz.ps1 PowerShell Pop Quiz Given this short cmdlet description: Creates a new System.Windows.Markup.RoutedEventConverter What command would you use? [1] Invoke-CauScan [2] Clear-Tpm [3] Remove-WindowsCapability [4] Get-IscsiTargetServerSetting [5] New-RoutedEventConverter Select a menu choice [1-5]: 5 You are Correct!! Do you want another question? Press any key to continue or Q to quit: q Your scored 1 correct out of 1 [100.00 %] .Example PS C:\> c:\scripts\PSQuiz.ps1 -module Hyper-V Launch the quiz but only use commands from the Hyper-V module. .Example PS C:\> c:\scripts\PSQuiz.ps1 -exclude ISE,WPK,my* Launch the quiz using all modules except ISE, WPK and any modules that start with 'my'. .Link Get-Help .Link Get-Command .Notes Version 0.9 Learn more about PowerShell:Essential PowerShell Learning Resources#> [cmdletbinding(DefaultParameterSetName = "all")] Param( [Parameter(ParameterSetName = "single")] [ValidateScript( {Get-Module $_ -list})] #You can specify a single module for testing. The default is all modules. [string]$ModuleName, [Parameter(ParameterSetName = "all")] #Enter a comma separated list of module names to ignore. You can use wildcards. [string[]]$Exclude, #This is used to indicate a continuing test. You should never need to use this parameter. [switch]$NextQuestion ) Clear-Host Write-Verbose "Starting $($myinvocation.MyCommand)" Write-Verbose "PSBoundparameters:" write-verbose ($psboundparameters | out-string) if (-Not $NextQuestion) { Write-Verbose "First question setup" #initialize some variables to keep track of correct answers $global:QuestionCount = 0 $global:CorrectCount = 0 $global:CommandCache = @() if ($ModuleName) { $Status = "Getting commands from module $modulename." Write-Verbose $Status Write-Progress -Activity $myinvocation.MyCommand -Status $status -CurrentOperation "Please wait..." $global:CommandCache = Get-command -CommandType Cmdlet,Function -Module $ModuleName } else { $status = "Getting commands from all available modules." if ($exclude) { $status += " Excluding these modules: $($exclude -join ',') " #define a separate filter because this causes a problem in PowerShell v6 if $exclude is not specified $filter = {$_.Source -AND $_.source -notmatch $($exclude -join '|')} } else { $filter = {$_.Source} } Write-Verbose $status Write-Progress -Activity $myinvocation.MyCommand -Status $status -CurrentOperation "Please wait..." #get cmdlets and function that have a defined source which should be a module or snapin $global:CommandCache = (Get-command -CommandType Cmdlet,Function).Where($filter) } Write-Progress -Activity $myinvocation.MyCommand -Completed Write-Verbose "Added $(($global:commandCache).count) commands to the command cache" if ((($global:commandCache).count) -eq 0) { Write-Warning "Failed to find commands in any matching modules." #bail out Return } } else { Write-Verbose "Continuing the quiz" Write-Verbose "Current question count: $($global:QuestionCount)" Write-Verbose "Current correct count: $($global:CorrectCount)" Write-Verbose "Current command cache: $(($global:commandCache).count)" } #select a random command with a legitimate synopsis Write-Verbose "Selecting a random command" do { $cmd = $global:CommandCache | Get-Random $synopsis = ($cmd | Get-Help).synopsis } until ($synopsis -notmatch "(This cmdlet is not supported)|\[|(Fill in the Synopsis)" -AND $synopsis -match "\w{4,}") #get other noun related commands [object[]]$commands = @($cmd) $commands += get-command -noun $cmd.noun | Where-Object {$_.name -ne $cmd.name} | Select-Object -first 4 #get additional random commands if there are not enough noun-related if ($commands.count -lt 5) { Write-Verbose "Getting supplemental commands for answers" While ($commands.count -lt 5) { $add = Get-command -CommandType Cmdlet | Get-Random | Where-Object {$commands.name -notcontains $_.name} $commands += $add } } #randomize $commands = $commands | get-random -count $commands.count $Title = "PowerShell Pop Quiz" $Cue = @" Given this short cmdlet description: $synopsis What command would you use? "@ for ($i = 1; $i -lt $commands.count + 1; $i++) { $Cue += " [$i]`t$($commands[$i-1].Name)`n" } Write-host $Title -ForegroundColor Cyan Write-Host $Cue -ForegroundColor Yellow Do { try { [validaterange(1, 5)][int32]$r = Read-Host -prompt "Select a menu choice [1-5]" -erroraction stop write-verbose "You entered $r" } Catch { #ignore the error write-warning $_.exception.message $r = 0 } } Until ($r -ge 1 -AND $r -le 5) #increment the question counter $global:QuestionCount++ if ($commands[$r - 1].name -eq $cmd.Name) { $global:CorrectCount++ Write-Host "`nYou are Correct!!" -foregroundcolor green } else { Write-Host "`nThe correct answer is $($cmd.name)" -foregroundcolor Red } [string]$s = Read-Host "`nDo you want another question? Press any key to continue or Q to quit" if ($s -match "^q") { $score = ($global:CorrectCount / $global:QuestionCount) $result = "Your scored {0} correct out of {1} [{2:p2}]" -f $global:CorrectCount, $global:QuestionCount, $score #colorize output based on score if ($score -ge .80) { $fg = "green" } elseif ($score -ge .40) { $fg = "yellow" } else { $fg = "red" } Write-Host $result -ForegroundColor $fg Remove-Variable CorrectCount, QuestionCount,CommandCache -Scope global } else { if (-Not ($psboundParameters.containsKey("NextQuestion"))) { Write-Verbose "Flagging for next question" $psboundparameters.add("NextQuestion", $True) } Write-Verbose "Invoking quiz" #Write-verbose ($myinvocation.mycommand | out-string) &$($myinvocation.mycommand) @PSBoundParameters }
I hope you'll give it a shot and if you get errors, let me know. Also interested in any enhancements that would make this more useful. I have a few in mind already but I'd like to get some feedback on the core functionality.
I would love to see this where we could upload specific commands to go along with certain training to help students prep for exams or certifications, i.e. PowerShell commands specific to Windows 10 or Windows Server 2016 certs.
I have not tried running it yet but, this looks great for quizzing on PowerShell in general.
I have some other ideas I am kicking around.