OpsaC - Operating as PowerShell code
published: January 29, 2019 author: Tinu tags: PowerShell categories: PowerShell-Basic
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.
Enable -WhatIf
and -Confirm
in the [CmdletBinding(SupportsShouldProcess)]
of your functions.
function Test-MyPing{
## By specifying SupportsShouldProcess in this way, we can now call our function with -WhatIf (or -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, the foreach-loop checks for each given parameter
foreach($item in $PSBoundParameters.keys){ $params = "$($params) -$($item) $($PSBoundParameters[$item])" }
## $PSCmdlet.ShouldProcess($params.Trim()) checks for the -WhatIf (and -Confirm parameter)
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
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"
}
}
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
}
}
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{
}
}
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
}
}
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
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'
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 $_}
}
About Functions, Everything you wanted to know about ShouldProcess on Microsoft Docs