Tinus EngOps Wiki

Logo

OpsaC - Operating as PowerShell code

Links

Home

PowerShell Blog

PowerShell Index

PowerShell Search

Additional Websites

View my GitHub Profile

View my GitHub Gists

View Tinus IT Wiki

View my Photo Website

Function

tags: PowerShell categories: PowerShell-Basic

Table of Contents

Function Names

You can assign any name to a function, but functions that you share with others should follow the naming rules that have been established for all PowerShell commands.

Functions names should consist of a verb-noun pair in which the verb identifies the action that the function performs and the noun identifies the item on which the cmdlet performs its action.

Functions should use the standard verbs that have been approved for all PowerShell commands. These verbs help us to keep our command names simple, consistent, and easy for users to understand.

For more information about the standard PowerShell verbs, see Approved Verb in the Microsoft Docs.

Functions names should have your prefix between the verb and noun so that an existing function with the same name is not overwritten.

WhatIf and Confirm

Enable -WhatIf and -Confirm in the [CmdletBinding(SupportsShouldProcess)] of your functions.

function Test-MyPing{
    # enable WhatIf and Confirm
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(
          Mandatory=$true,
          ValueFromPipeline = $true,
          ValueFromPipelineByPropertyName = $true,
          Position = 0
        )]
        [ValidateLength(4,255)]
        [String[]] $Destination
    )    
    begin {
        $function = $($MyInvocation.MyCommand.Name)
        Write-Verbose "Running $function"
        $resultset = @()
    }

    process {
        # ShouldProcess as close to the change as possible
        foreach($item in $PSBoundParameters.keys){ $params = "$($params) -$($item) $($PSBoundParameters[$item])" }
        if ($PSCmdlet.ShouldProcess($params.Trim())) {
            # here to place your code to proceed
            Test-PsNetPing -Destination $Destination
        }
    }

    end {
        return $resultset
    }
}
Test-MyPing -Destination 'google.com' -WhatIf

What if: Performing the operation "Test-MYPing" on target "google.com".
Test-MyPing -Destination 'google.com' -Confirm

Confirm
Are you sure you want to perform this action?
Performing the operation "Test-MYPing" on target "google.com".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): y

IcmpSucceeded     : True
IPAddress         : 2a00:1450:400a:808::200e
BytesSend         : 32
BytesReceived     : 32
TimeStamp         : 2023-04-29 12:58:28.644
Destination       : google.com
StatusDescription : ICMP Success
MinTimeout        : 0
MaxTimeout        : 1000
TimeMs            : 5

Function with parameters

A function which returns an object, either if an error occure or not.

<#
.SYNOPSIS
Short description

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Get-MWASomething{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            Position = 0
        )]
        [String[]] $InputString,

        [Parameter(Mandatory=$false)]
        [Int] $Timeout = 10,

        [Parameter(Mandatory=$false)]
        [Switch] $Fire
    )

    begin{
        $StartTime = Get-Date
        $function = $($MyInvocation.MyCommand.Name)
        foreach($item in $PSBoundParameters.keys){
            $params = "$($params) -$($item) $($PSBoundParameters[$item])"
        }
        Write-Verbose "[Begin] $($function)$($params)"
        $ret = @()
    }

    process{
        Write-Verbose "[Process] $($function)"
        $ret = foreach($item in $InputString){
            try{
                [PSCustomObject]@{
                    Succeeded   = $true
                    InputString = $item
                    Timeout     = $Timeout
                    Fire        = $Fire
                }
            }        
            catch{
                Write-Verbose "-> Catch block reached"
                [PSCustomObject]@{
                    Succeeded   = $false
                    InputString = $item
                    Timeout     = $Timeout
                    Fire        = $Fire
                }
                # Build the Error-Object
                $ErrorObject = [PSCustomObject]@{
                    Succeeded  = $false
                    Function   = $function
                    Scriptname = $($_.InvocationInfo.ScriptName)
                    LineNumber = $($_.InvocationInfo.ScriptLineNumber)
                    Activity   = $($_.CategoryInfo).Activity
                    Message    = $($_.Exception.Message)
                    Category   = $($_.CategoryInfo).Category
                    Exception  = $($_.Exception.GetType().FullName)
                    TargetName = $($_.CategoryInfo).TargetName
                }
                # Write some info to the screen
                Write-Warning "Function: $($ErrorObject.Function), LineNumber: $($ErrorObject.LineNumber), Message: $($ErrorObject.Message)"
                $error.clear()
            }
        }
        return $ret
    }

    end{
        Write-Verbose "[End] $($function)"
        Write-Host "Duration: $(New-TimeSpan -Start $StartTime -End (Get-Date) | % { "{1:0}h {2:0}m {3:0}s {4:000}ms" -f $_.Days, $_.Hours, $_.Minutes, $_.Seconds, $_.Milliseconds })`n"
    }
}

Function with ValidateSet

A function with ValidateSet to choose a parameter value.

<#
.SYNOPSIS
Short description

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Get-MWASomething{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [ValidateSet("Medium","Hot","Extra Hot")]
        [Parameter(Mandatory=$true)]
        [String] $Spice,

        [Parameter(Mandatory=$true)]
        [Int] $Timeout,

        [Parameter(Mandatory=$true)]
        [Switch] $Fire
    )

    begin{
        $function = $($MyInvocation.MyCommand.Name)
        foreach($item in $PSBoundParameters.keys){
            $params = "$($params) -$($item) $($PSBoundParameters[$item])"
        }
        Write-Verbose "Running $($function)$($params)"
    }

    process{
        try{
            $ret = [PSCustomObject]@{
                Succeeded = $true
                Spice     = $Spice
                Timeout   = $Timeout
                Fire      = $Fire
            }
        }
        catch [Exception]{
            Write-Verbose "-> Catch block reached"
            $ret = [PSCustomObject]@{
                Succeeded  = $false
                Function   = $function
                Scriptname = $($_.InvocationInfo.ScriptName)
                LineNumber = $($_.InvocationInfo.ScriptLineNumber)
                Activity   = $($_.CategoryInfo).Activity
                Message    = $($_.Exception.Message)
                Category   = $($_.CategoryInfo).Category
                Exception  = $($_.Exception.GetType().FullName)
                TargetName = $($_.CategoryInfo).TargetName
            }
            $error.clear()
        }
        finally{
            Write-Verbose "-> Finally block reached"
        }
    }

    end{
        return $ret
    }
}

Function with ParameterSet

A function with a ParameterSet.

function Get-MWASomething{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        #region parameterset
        [Parameter(Mandatory = $false, ParameterSetName = 'File')]
        [Switch] $File,

        [Parameter(Mandatory = $false, ParameterSetName = 'Console')]
        [Switch] $Console,

        [Parameter(Mandatory = $false, ParameterSetName = 'Eventlog')]
        [Switch] $Eventlog,
        #endregion

        #region parameterset Logfile
        [Parameter(Mandatory = $true, ParameterSetName = 'File')]
        [String] $Logfile,

        [Parameter(Mandatory = $false, ParameterSetName = 'File')]
        [Switch] $Markdown,

        [Parameter(Mandatory = $false, ParameterSetName = 'File')]
        [Switch] $CSV,
        #endregion

        #region parameterset Eventlog
        [Parameter(Mandatory = $true, ParameterSetName = 'Eventlog')]
        [String] $Logname,
        #endregion

        [Parameter(Mandatory = $true)]
        [String] $Message

    )

    begin{
        $function = $($MyInvocation.MyCommand.Name)
        foreach($item in $PSBoundParameters.keys){
            $params = "$($params) -$($item) $($PSBoundParameters[$item])"
        }
        Write-Verbose "Running $($function)$($params)"
    }

    process{
        Switch($PsCmdlet.ParameterSetName){
            "File" {
                if($Markdown){
                    Write-Host "Send Message to Markdown-File: $Logfile $Message"
                }elseif($CSV){
                    Write-Host "Send Message to CSV-File: $Logfile $Message"
                }else{
                    Write-Host "Send Message to Text-File: $Logfile $Message"
                }
                break
            }
            "Console" {
                Write-Host "Send Message to Console: $Message"
                break
            }
            "Eventlog" {
                Write-Host "Send Message to Eventlog: $Logname $Message"
                break
            }
        }
    }

    end{

    }
}

Function with Dynamic Parameter

First, create a JSON-File with values like this and save it as C:\Values.json:

{
    "PRD": [
                "vceprdsrv1.company.local",
                "vceprdsrv2.company.local",
            ],
    "INT":  [
                "vceintsrv1.company.local",
                "vceintsrv2.company.local",
            ],
    "DEV":  [
                "vcedevsrv1.company.local",
                "vcedevsrv2.company.local",
            ]
}

A function with a dynamic parameter instead of a ValidateSet.

<#
.SYNOPSIS
Short description

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Get-MWASomething{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [ValidateSet("PRD","INT","DEV")]
        [Parameter(Mandatory=$true)]
        [String] $VCEnv
    )
    DynamicParam {
        # Set the dynamic parameters' name
        $ParameterName = 'VIServer'

        # Create the dictionary
        $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        # Create the collection of attributes
        $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Create and set the parameters' attributes
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $ParameterAttribute.Position = 0

        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute)

        # Generate and set the ValidateSet-Values from the JSON-file
        $content = Get-Content -Path C:\Values.json | ConvertFrom-Json
        $ValidateSetValue = switch($VCEnv){
            'PRD'  {$content.PRD}
            'INT'  {$content.INT}
            'DEV'  {$content.DEV}
        }
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($ValidateSetValue)

        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)

        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
        $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
        return $RuntimeParameterDictionary
    }

    begin{
        $function = $($MyInvocation.MyCommand.Name)
        foreach($item in $PSBoundParameters.keys){
            $params = "$($params) -$($item) $($PSBoundParameters[$item])"
        }
        Write-Verbose "Running $($function)$($params)"
        $Path = $PSBoundParameters['Path']
    }

    process{
        try{
            $ret = [PSCustomObject]@{
                Succeeded = $true
                vCenterEnvironment = $VCEnv
                vCenterServer      = $RuntimeParameterDictionary.VIServer.Value
            }
        }
        catch [Exception]{
            Write-Verbose "-> Catch block reached"
            $ret = [PSCustomObject]@{
                Succeeded  = $false
                Function   = $function
                Scriptname = $($_.InvocationInfo.ScriptName)
                LineNumber = $($_.InvocationInfo.ScriptLineNumber)
                Activity   = $($_.CategoryInfo).Activity
                Message    = $($_.Exception.Message)
                Category   = $($_.CategoryInfo).Category
                Exception  = $($_.Exception.GetType().FullName)
                TargetName = $($_.CategoryInfo).TargetName)
            }
            $error.clear()
        }
        finally{
            Write-Verbose "-> Finally block reached"
        }
    }
    end{
        return $ret
    }
}

Function with Object

A Function with one Object as parameter can have a lot of properties which can be used as parameters.

<#
.SYNOPSIS
Short description

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Get-MWAErrorInEventlog{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(Mandatory=$true)]
        [Object] $args
    )
    $ret = $null
    try{
        $ret = Get-WinEvent -MaxEvents 5 -FilterHashtable @{
            Logname   = $args.LogName
            StartTime = [DateTime]$args.StartTime
            EndTime   = [DateTime]$args.EndTime
            Level     = [Int32]$args.Level
        }
    }
    catch [Exception]{
        Write-Verbose "-> Catch block reached"
        $ret = [PSCustomObject]@{
            Succeeded  = $false
            Function   = $function
            Scriptname = $($_.InvocationInfo.ScriptName)
            LineNumber = $($_.InvocationInfo.ScriptLineNumber)
            Activity   = $($_.CategoryInfo).Activity
            Message    = $($_.Exception.Message)
            Category   = $($_.CategoryInfo).Category
            Exception  = $($_.Exception.GetType().FullName)
            TargetName = $($_.CategoryInfo).TargetName
        }
        $error.clear()
    }
    finally{
        Write-Verbose "-> Finally block reached"
    }
    return $ret
}

params = @{
    LogName   = 'Microsoft-Windows-WindowsUpdateClient/Operational'
    MaxEvents = 5
    StartTime = (get-date).AddDays(-20)
    EndTime   = get-date
    Level     = 2
}
Get-MWAErrorInEventlog -args $params

Logging Function

function Write-MWALogfile{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(Mandatory=$true)]
        [string] $LogFile,

        [ValidateSet("ERROR","WARNING","INFO")]
        [Parameter(Mandatory=$true)]
        [string] $Status,

        [Parameter(Mandatory=$true)]
        [string] $Message

    )

    #region Test is logfile greater than 4 mb
    if (Test-Path $LogFile){
        $LogFileProperty = Get-Item $LogFile
        $LogFileSizeMB   = $LogFileProperty.Length / 1mb
        if($LogFileSizeMB -gt 4){
            Rename-Item -Path $LogFile -NewName "$($LogFileProperty.Name)_$(Get-Date -f 'yyyyMMddHHmmss').log"
        }
    }  
    #endregion

    #region write loginformation
    if (-not(Test-Path $LogFile)){$null = New-Item $Logfile -type file}
    switch($Status){
        'ERROR'   {$LogStatus = '[   ERROR   ]'}
        'WARNING' {$LogStatus = '[  WARNING  ]'}
        'INFO'    {$LogStatus = '[INFORMATION]'}
    }
    $DateNow   = Get-Date -Format "dd.MM.yyyy HH:mm:ss.fff"
    $FileInput = "$($DateNow) | $($LogStatus) | $($Message)"
    Add-Content $LogFile -value $FileInput
    #endregion
}

$CurrentScriptFile = $PSCommandPath -replace '.ps1', '.log'
Write-MWALogfile -LogFile $CurrentScriptFile -Status WARNING -Message '[START] Script'

List all arguments passed to function

If you want print out all your passed arguments, type the following code:

$PSBoundParameters | Out-String | foreach {Write-Verbose $_}

But if you give an object as argument, then you need the following code:

foreach($item in $PSBoundParameters.Keys) {
    $PSBoundParameters[$item] | Out-String | % {Write-Verbose $_}
}

See also

About Functions on Microsoft Docs

[ Top ] [ Blog ]