Skip to content

Instantly share code, notes, and snippets.

@jdhitsolutions
Created April 6, 2023 03:19
Show Gist options
  • Save jdhitsolutions/6463ea32d91e3ea21eb8faffeffd5a47 to your computer and use it in GitHub Desktop.
Save jdhitsolutions/6463ea32d91e3ea21eb8faffeffd5a47 to your computer and use it in GitHub Desktop.

Revisions

  1. jdhitsolutions created this gist Apr 6, 2023.
    809 changes: 809 additions & 0 deletions rtpsug-advparameters.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,809 @@
    #requires -version 7.3

    <#
    No code in this file should be considered production-ready.
    All code and explanations should be viewed as educational material.
    You are free to re-use anything in this file in your own work.
    #>

    # FIND ME: https://jdhitsolutions.github.io/

    return 'This is a demo script file. Load this file in your scripting editor and execute selected lines.'
    # Some things will work in Windows PowerShell with a few changes.

    #region advanced functions defined

    # [cmdletbinding()]
    #Begin/Process/End script blocks
    #typically accept pipeline input

    #endregion
    #region Parameter considerations

    # What needs a parameter?
    # What's in a name?
    # - Don't re-invent the wheel with parameter names
    # - Simple alphabetical names
    # - Consider a prefix
    # - User proper case or camel case and be consistent
    # Do you need a default value?
    # Type matters

    Function Get-FolderSize {
    [cmdletbinding()]
    [OutputType('folderSize')]
    [alias('gfs')]
    Param(
    [string[]]$Path = '.',
    [Switch]$Recurse
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    if ($Recurse) {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Recursing"
    $option = 'AllDirectories'
    }
    else {
    $option = 'TopDirectoryOnly'
    }
    } #begin

    Process {
    foreach ($folder in $path) {
    $p = Get-Item -Path $folder
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($p.FullName) and $($p.GetDirectories().count) top-level folders"
    $stats = $p.GetFiles('*', $option) | Measure-Object -Property length -Sum

    [PSCustomObject]@{
    PSTypeName = 'folderSize'
    Path = $p.FullName
    Size = $stats.sum
    Files = $stats.count
    Directories = $p.GetDirectories('*', $option).count
    ComputerName = [System.Environment]::MachineName
    Date = Get-Date
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-FolderSize

    #alternative
    Function Get-FolderSize {
    [cmdletbinding()]
    [OutputType('folderSize')]
    [alias('gfs')]
    Param(
    #notice the change in parameter type
    #this type needs additional help to use properly since
    #it won't work with non-Filesystem paths
    [System.IO.DirectoryInfo[]]$Path = '.',
    [Switch]$Recurse
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    if ($recurse) {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Recursing"
    $option = 'AllDirectories'
    }
    else {
    $option = 'TopDirectoryOnly'
    }
    } #begin

    Process {
    foreach ($folder in $path) {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($folder.FullName) and $($folder.GetDirectories().count) top-level folders"
    $stats = $folder.GetFiles('*', $option) | Measure-Object -Property length -Sum

    [PSCustomObject]@{
    PSTypeName = 'folderSize'
    Path = $folder.FullName
    Size = $stats.sum
    Files = $stats.count
    Directories = $folder.GetDirectories('*', $option).count
    ComputerName = [System.Environment]::MachineName
    Date = Get-Date
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-FolderSize

    Function Set-SecretFile {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('None', 'PSCustomObject')]
    [alias('ssf')]
    Param(
    [Parameter()]
    #not using string type for the file
    [ValidateScript({ $_.Exists })]
    [System.IO.FileInfo]$FilePath,
    #Encrypt or Decrypt
    [string]$FileOption = 'Encrypt',
    [switch]$PassThru
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $FilePath"

    if ($PSCmdlet.ShouldProcess($filepath, $FileOption)) {
    Switch ($FileOption) {
    'Encrypt' {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Encrypt"
    $filePath.Encrypt()
    }
    'Decrypt' {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Decrypt"
    $filePath.Decrypt()
    }
    } #Switch
    if ($PassThru) {
    $name = $FilePath.FullName.replace('\', '\\')
    Get-CimInstance -ClassName CIM_DataFile -Filter "name='$Name'" | Select-Object Name, FileSize, LastModified, Encrypted
    }
    } #whatIf
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Set-SecretFile

    # using FileInfo automatically adds tab-completion
    # BUT --- this won't work with PSDrive file paths

    #endregion
    #region Parameter attribute

    # [Parameter()]
    # position
    # mandatory
    # helpmessage
    # pipeline values
    # alias [alias()]

    Function Get-FolderSize {
    [cmdletbinding()]
    [OutputType('folderSize')]
    [alias('gfs')]
    Param(
    [Parameter(
    Position = 0,
    ValueFromPipeline,
    ValueFromPipelineByPropertyName,
    HelpMessage = 'Specify a folder to analyze.'
    )]
    [alias('folder')]
    [System.IO.DirectoryInfo[]]$Path = '.',
    [Switch]$Recurse
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    if ($recurse) {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Recursing"
    $option = 'AllDirectories'
    }
    else {
    $option = 'TopDirectoryOnly'
    }
    } #begin

    Process {
    foreach ($folder in $path) {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($folder.FullName) and $($folder.GetDirectories().count) top-level folders"
    $stats = $folder.GetFiles('*', $option) | Measure-Object -Property length -Sum

    [PSCustomObject]@{
    PSTypeName = 'folderSize'
    Path = $folder.FullName
    Size = $stats.sum
    Files = $stats.count
    Directories = $folder.GetDirectories('*', $option).count
    ComputerName = [System.Environment]::MachineName
    Date = Get-Date
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-FolderSize

    Get-FolderSize -folder c:\work
    [PSCustomObject]@{path = 'c:\work' }, [PSCustomObject]@{path = 'c:\windows' } |
    Get-FolderSize -verbose
    Get-ChildItem c:\work -Directory | Get-FolderSize | Format-Table

    #endregion
    #region Parameter validation

    #Validate Script
    Function Get-FolderSize {
    [cmdletbinding()]
    [OutputType('folderSize')]
    [alias('gfs')]
    Param(
    [Parameter(
    Position = 0,
    ValueFromPipeline,
    ValueFromPipelineByPropertyName,
    HelpMessage = 'Specify a folder to analyze.'
    )]
    [alias('Folder')]
    [ValidateScript({ $_.Exists })] # <------
    [System.IO.DirectoryInfo[]]$Path = '.',
    [Switch]$Recurse
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    if ($recurse) {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Recursing"
    $option = 'AllDirectories'
    }
    else {
    $option = 'TopDirectoryOnly'
    }
    } #begin

    Process {
    foreach ($folder in $path) {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($folder.FullName) and $($folder.GetDirectories().count) top-level folders"
    $stats = $folder.GetFiles('*', $option) | Measure-Object -Property length -Sum

    [PSCustomObject]@{
    PSTypeName = 'folderSize'
    Path = $folder.FullName
    Size = $stats.sum
    Files = $stats.count
    Directories = $folder.GetDirectories('*', $option).count
    ComputerName = [System.Environment]::MachineName
    Date = Get-Date
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-FolderSize

    Function Get-FolderSize {
    [cmdletbinding()]
    [OutputType('folderSize')]
    [alias('gfs')]
    Param(
    [Parameter(
    Position = 0,
    ValueFromPipeline,
    ValueFromPipelineByPropertyName,
    HelpMessage = 'Specify a folder to analyze.'
    )]
    [alias('Folder')]
    #PowerShell 7
    [ValidateScript({ $_.Exists }, ErrorMessage = 'Cannot validate that {0} is a valid directory object.')]
    [System.IO.DirectoryInfo[]]$Path = '.',
    [Switch]$Recurse
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    if ($recurse) {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Recursing"
    $option = 'AllDirectories'
    }
    else {
    $option = 'TopDirectoryOnly'
    }
    } #begin

    Process {
    foreach ($folder in $path) {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($folder.FullName) and $($folder.GetDirectories().count) top-level folders"
    $stats = $folder.GetFiles('*', $option) | Measure-Object -Property length -Sum

    [PSCustomObject]@{
    PSTypeName = 'folderSize'
    Path = $folder.FullName
    Size = $stats.sum
    Files = $stats.count
    Directories = $folder.GetDirectories('*', $option).count
    ComputerName = [System.Environment]::MachineName
    Date = Get-Date
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-FolderSize

    #with custom error in PowerShell 7
    Get-FolderSize C:\work\a.ps1
    Get-FolderSize Z:\foo

    #validate set
    Function Set-SecretFile {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('None', 'PSCustomObject')]
    [alias('ssf')]
    Param(
    [Parameter(
    Position = 0,
    Mandatory,
    ValueFromPipeline,
    ValueFromPipelineByPropertyName,
    HelpMessage = 'Specify the file to encrypt or decrypt'
    )]
    #not using string type for the file
    [ValidateScript({ $_.Exists }, ErrorMessage = "Can't find or verify {0} exists as a file.")]
    [alias('FullName')]
    [System.IO.FileInfo]$FilePath,
    [Parameter(Position = 1)]
    [ValidateSet('Encrypt', 'Decrypt')] #<-----
    [string]$FileOption = 'Encrypt',
    [Switch]$PassThru
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $FilePath"

    if ($PSCmdlet.ShouldProcess($filepath, $FileOption)) {
    Switch ($FileOption) {
    'Encrypt' {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Encrypt"
    $filePath.Encrypt()
    }
    'Decrypt' {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Decrypt"
    $filePath.Decrypt()
    }
    } #Switch
    if ($PassThru) {
    $name = $FilePath.FullName.replace('\', '\\')
    Get-CimInstance -ClassName CIM_DataFile -Filter "name='$Name'" | Select-Object Name, FileSize, LastModified, Encrypted
    }
    } #whatIf
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Set-SecretFile

    help Set-SecretFile
    Set-SecretFile -FileOption <tab>

    Get-ChildItem c:\work\*.txt | Set-SecretFile -FileOption Encrypt -WhatIf

    #validatePattern
    Function Get-PSScriptStat {
    [cmdletbinding()]
    [OutputType('psScriptStat')]
    Param(
    [Parameter(
    Position = 0,
    Mandatory,
    ValueFromPipeline,
    HelpMessage = 'Specify a PowerShell script file.'
    )]
    [ValidateNotNullOrEmpty()]
    #I could integrate the pattern with the test
    [ValidateScript({ Test-Path $_ }, ErrorMessage = 'Cannot find or validate {0}.')]
    [ValidatePattern('\.ps(m)?1$')] #<-----
    [string]$Path
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $Path"
    $stat = Get-Content $Path -Raw | Measure-Object -Word -Line
    $item = Get-Item $Path
    [PSCustomObject]@{
    Path = $item.FullName
    Size = $item.Length
    Modified = $item.LastWriteTime
    Words = $stat.Words
    Lines = $stat.Lines
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-PSScriptStat

    Get-ChildItem c:\scripts\ -File | Get-Random -Count 10 | Get-PSscriptStat | Format-Table
    #validateRange and ValidateCount
    Function New-TestData {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('System.IO.FileInfo')]
    Param(
    [Parameter(
    Position = 0,
    Mandatory,
    ValueFromPipeline,
    HelpMessage = 'Specify the path for the test data files.'
    )]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({ Test-Path $_ }, ErrorMessage = 'Cannot validate that {0} is a valid directory.')]
    [string]$Path,

    [Parameter(HelpMessage = 'Specify a collection of extensions like foo or bar, without the period. The limit is 5.')]
    [ValidateCount(1, 5)]
    [string[]]$Extension = @('dat', 'txt', 'log'),

    [Parameter(HelpMessage = 'Specify the maximum size in bytes between 10 and 5MB')]
    [ValidateRange(10, 5242880)] #<-----
    [int32]$MaximumSize = 10,

    [Parameter(HelpMessage = 'Specify the number of test files to create between 1 and 25')]
    [ValidateRange(1, 25)] #<-----
    [int]$Count = 10
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Creating $Count test files in $Path "
    1..$Count | ForEach-Object {
    $Size = ($MaximumSize -gt 10) ? ( Get-Random -Minimum 10 -Maximum $MaximumSize) : 10
    $ext = $Extension | Get-Random -Count 1
    $FileName = [System.IO.Path]::GetRandomFileName() -replace '\w{3}$', $ext
    $OutPut = Join-Path -Path $Path -ChildPath $FileName
    #get a random creation time
    $Created = (Get-Date).AddHours( - (Get-Random -min 1 -Maximum 1000))
    #get a random LastWriteTime
    $Modified = $Created.AddHours((Get-Random -Minimum 1 -Maximum 995))

    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] ... $Output [$size]"
    if ($PSCmdlet.ShouldProcess("$Output [$size]")) {
    $stream = New-Object System.IO.FileStream("$Output", [System.IO.FileMode]::CreateNew)
    [void]$stream.Seek($Size, [System.IO.SeekOrigin]::Begin)
    $stream.WriteByte(0)
    $Stream.Close()
    Start-Sleep -Milliseconds 500
    $f = Get-Item -Path $OutPut
    $f.CreationTime = $Created
    $f.CreationTimeUtc = $Created.ToUniversalTime()
    $f.LastWriteTime = $Modified
    $f.LastWriteTimeUTC = $modified.ToUniversalTime()
    $f.LastAccessTime = $Modified
    $f.LastAccessTimeUTC = $modified.ToUniversalTime()
    $f
    }
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close New-TestData

    New-TestData d:\temp -Count 100 -MaximumSize 1mb -WhatIf
    New-TestData d:\temp -Count 5 -MaximumSize 1mb -WhatIf
    New-TestData d:\temp -Count 10 -Extension 'foo', 'bar', 'dat' -MaximumSize 10kb

    #endregion
    #region ArgumentCompleter

    #using ValidateSet gives you auto-complete
    Function Get-LogInfo {
    [CmdletBinding()]
    Param(
    [Parameter(Position = 0, HelpMessage = 'Specify a log name')]
    [ValidateSet('System', 'Application', 'Windows PowerShell')] #<-----
    [string]$Log = 'System',

    [ValidateRange(1, 1000)]
    [alias('max')]
    [int]$Count = 100
    )
    Get-WinEvent -FilterHashtable @{
    LogName = $log
    Level = 2, 3
    } -MaxEvents $count | Group-Object -Property ProviderName -NoElement |
    Sort-Object -Property Count -Descending
    } #end function

    # Get-LogInfo <tab>

    #creating a custom argument completer
    Function Get-PSScriptStat {
    [cmdletbinding()]
    [OutputType('psScriptStat')]
    Param(
    [Parameter(
    Position = 0,
    ValueFromPipeline,
    HelpMessage = 'Specify a PowerShell script file.'
    )]
    [ValidateNotNullOrEmpty()]
    #this should run quickly
    [ArgumentCompleter({ (Get-ChildItem .\*.ps1, .\*.psm1).Name | Sort-Object })]
    #I could integrate the pattern with the test
    [ValidateScript({ Test-Path $_ }, ErrorMessage = 'Cannot find or validate {0}.')]
    [ValidatePattern('\.ps(m)?1$')]
    [string]$Path
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $Path"
    $stat = Get-Content $Path -Raw | Measure-Object -Word -Line
    $item = Get-Item $Path
    [PSCustomObject]@{
    Path = $item.FullName
    Size = $item.Length
    Modified = $item.LastWriteTime
    Words = $stat.Words
    Lines = $stat.Lines
    }
    } #process

    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end

    } #close Get-PSScriptStat

    #Get-PSScriptStat <tab>
    # show PSReadline completions

    #endregion
    #region Parameter sets

    Function Get-LogInfo {
    [CmdletBinding(DefaultParameterSetName = 'computer')]
    [OutputType('WinEventLogInfo')]
    Param(
    [Parameter(Position = 0, HelpMessage = 'Specify a log name')]
    [ValidateSet('System', 'Application', 'Windows PowerShell')]
    [string]$Log = 'System',

    [ValidateRange(1, 1000)]
    [alias('max')]
    [int]$Count = 100,

    [Parameter(ValueFromPipeline, ParameterSetName = 'computer')]
    [string]$Computername = $env:COMPUTERNAME,

    [Parameter(ParameterSetName = 'computer')]
    [PSCredential]$Credential,

    [Parameter(ValueFromPipeline, ParameterSetName = 'session')]
    [ValidateNotNullOrEmpty()]
    [alias('session')]
    [System.Management.Automation.RunSpaces.PSSession]$PSSession
    )

    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    $scriptblock = {
    Write-Host "Querying $using:log on $env:computername for $using:count errors and warnings" -ForegroundColor cyan
    Get-WinEvent -FilterHashtable @{
    LogName = $using:log
    Level = 2, 3
    } -MaxEvents $using:count | Group-Object -Property ProviderName -NoElement |
    Sort-Object -Property Count -Descending
    }
    #parameters to splat to Invoke-Command
    $icm = @{
    scriptblock = $scriptblock
    }
    if ($Credential) {
    $icm.Add('Credential', $Credential)
    }
    } #begin
    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Detected parameter set $($PSCmdlet.ParameterSetName)"
    if ($PSCmdlet.ParameterSetName -eq 'computer') {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $Log on $Computername"
    $icm['Computername'] = $Computername
    }
    else {
    $icm['Session'] = $PSSession
    }
    Invoke-Command @icm | ForEach-Object {
    [PSCustomObject]@{
    PSTypeName = 'WinEventLogInfo'
    LogName = $Log
    Count = $_.Count
    Source = $_.Name
    Computername = $_.PSComputername.ToUpper()
    }
    }

    } #process
    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end
    } #end function

    help Get-LogInfo
    Get-LogInfo
    get-loginfo Application -Verbose -Count 1000 -computername win10 #-Credential company\artd

    New-PSSession srv1, srv2, dom1 #-Credential company\artd
    $r = Get-PSSession | Get-LogInfo System -Count 500 -verbose
    $r | Format-Table -GroupBy Computername -Property Count, Source

    #endregion
    #region advanced voodoo if time

    #this is a proof-of-concept, not production-ready.
    Function Get-LogInfo {
    [CmdletBinding(DefaultParameterSetName = 'computer')]
    [OutputType('WinEventLogInfo')]
    Param(
    [ValidateRange(1, 1000)]
    [alias('max')]
    [int]$Count = 100,

    [Parameter(ValueFromPipeline, ParameterSetName = 'computer')]
    [ValidateNotNullOrEmpty()]
    [string]$Computername = $env:COMPUTERNAME,

    [Parameter(ParameterSetName = 'computer')]
    [PSCredential]$Credential,

    [Parameter(ValueFromPipeline, ParameterSetName = 'session')]
    [ValidateNotNullOrEmpty()]
    [alias('session')]
    [System.Management.Automation.RunSpaces.PSSession]$PSSession
    )
    DynamicParam {
    # Query for classic event logs on the specified computer
    If ($True) {
    $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

    # Defining parameter attributes
    $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
    $attributes = New-Object System.Management.Automation.ParameterAttribute
    $attributes.Position = 0
    $attributes.Mandatory = $True
    $attributes.HelpMessage = 'Select a classic event log'

    # Adding ValidateNotNullOrEmpty parameter validation
    $v = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute
    $AttributeCollection.Add($v)
    $attributeCollection.Add($attributes)

    # Adding ValidateSet parameter validation
    $splat = @{
    ListLog = '*'
    ErrorAction = 'SilentlyContinue'
    }
    if ($PSBoundParameters.ContainsKey('ComputerName')) {
    $splat.Add('Computername', $PSBoundParameters['Computername'])
    if ($PSBoundParameters.ContainsKey('Credential')) {
    $splat.Add('credential', $PSBoundParameters['credential'])
    }
    }
    $Value = (Get-WinEvent @splat).where({ $_.IsClassicLog -AND $_.RecordCount -gt 0 -AND $_.LogName -ne 'Security' }).LogName

    $vs = New-Object System.Management.Automation.ValidateSetAttribute($value)
    $AttributeCollection.Add($vs)

    # Defining the runtime parameter
    $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('Log', [String], $attributeCollection)
    #$dynParam1.Value = 'System'
    $paramDictionary.Add('Log', $dynParam1)
    return $paramDictionary
    } # end if
    } #end DynamicParam
    Begin {
    Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
    $scriptblock = {
    Param($LogName, $Max)
    Write-Host "Querying $LogName on $env:computername for $Max errors and warnings" -ForegroundColor cyan
    Get-WinEvent -FilterHashtable @{
    LogName = $LogName
    Level = 2, 3
    } -MaxEvents $Max | Group-Object -Property ProviderName -NoElement |
    Sort-Object -Property Count -Descending
    }

    #parameters to splat to Invoke-Command
    $icm = @{
    ScriptBlock = $scriptblock
    ArgumentList = @($PSBoundParameters['log'], $Count)
    }
    if ($Credential) {
    $icm.Add('Credential', $Credential)
    }
    } #begin
    Process {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Detected parameter set $($PSCmdlet.ParameterSetName)"
    $PSBoundParameters | Out-String | Write-Verbose
    if ($PSCmdlet.ParameterSetName -eq 'computer') {
    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $count $log on $($Computername.ToUpper())"
    $icm['Computername'] = $Computername
    }
    else {
    $icm['Session'] = $PSSession
    }
    Invoke-Command @icm | ForEach-Object {
    [PSCustomObject]@{
    PSTypeName = 'WinEventLogInfo'
    LogName = $PSBoundParameters['log']
    Count = $_.Count
    Source = $_.Name
    Computername = $_.PSComputername.ToUpper()
    }
    }

    } #process
    End {
    Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
    } #end
    } #end function

    # Get-LogInfo -computername dom1 -log <tab>

    #endregion
    #region learn more

    Install-Module PSScriptTools

    Get-ParameterInfo Get-LogInfo | Sort-Object ParameterSet

    <# Read the Help!
    about_Functions_Advanced
    about_Functions_Advanced_Methods
    about_Functions_Advanced_Parameters
    about_Functions_Argument_Completion
    about_Functions_CmdletBindingAttribute
    about_Functions_OutputTypeAttribute
    #>

    #endregion