Viewing entries tagged
parameter

PowerShell Quick Tip: Using ValidateSet

PowerShell Quick Tip: Using ValidateSet

PowerShell Quick Tip: Using ValidateSet

Why Use ValidateSet?

ValidateSet is part of advanced parameters. It allows you to constrain the input of the parameter to a set of values. It also will autocomplete those options for you as you tab through them!

This can really come in handy if you only want to accept a specific set of options as input to the parameter. I've found it useful in the following scenarios:

  • Scripts that work with AD, and you'd like a specific set of options for a given parameters
  • Scripts that work with an API, and only a specific set of values are accepted
  • Scripts that are used in web parsing where you'd like to constrain the options for a specific parameter
  • You want to reduce the amount of logic in your script, and offload it to parameter validation (added bonus: error generation for you)

Using ValidateSet

Using ValidateSet is easy! You just add the following line above your parameter:

[ValidateSet('Green','Blue','Red')]

The above example will ensure the input to the parameter we create is either Green, Blue, or Red.

Here is a simple function I put together to demonstrate how this works:

function Write-Color {
    [cmdletbinding()]
    param(       
        [Parameter(Mandatory)]  
        [ValidateSet('Green','Blue','Red')]
        [string]
        $color,
        $message
    )

    Write-Host $message -ForegroundColor $color 

}

When a function is in memory in the ISE, and ValidateSet is used, it will actually give you a visual list of the available options!

With this function in memory, let's run these commands and see what happens:

Write-Color -color Blue -Message "Validate: Blue"
Write-Color -color Red -Message "Validate: Red"
Write-Color -color Green -Message "Validate: Green"

That worked!

What if we used a color that's not in the group specified?

Write-Color -color DarkBlue -message "Validate: DarkBlue"

The command will also auto-complete the options for you in the console if you specify -color and then hit tab.

Limitations

There are some limitations when doing this. 

  • If you set a default value to one outside the array of options, it will work as it only checks incoming input
Hard to see, but it worked even though DarkBlue isn't in the set above!

Hard to see, but it worked even though DarkBlue isn't in the set above!

 

  • You're unable to generate your own error messages based on what happens in the function
    • This is fine, though, as you can wrap this up in a Try/Catch outside the function!
function Write-Color {
    [cmdletbinding()]
    param(       
        [Parameter()]  
        [ValidateSet('Green','Blue','Red')]
        [string]
        $color,
        $message
    )

    Write-Host $message -ForegroundColor $color 

}

Try {

    Write-Color -color Yellow -message "This will not work!" 

}

Catch [System.Management.Automation.ParameterBindingException] {

    $errorMessage = $_.Exception.Message
    Write-Host "Error: [$errorMessage]" -ForegroundColor Red -BackgroundColor DarkBlue
    <#

    Code to handle error

    #>
}

Error message after running:

Instead of using Write-Host, and merely showing the error, you'd want to have code in place that takes action based on the specific event.

Wrap Up

That's about it for using ValidateSet! It can really come in handy, and save you time when writing out your scripts.

Do you use ValidateSet? Leave a comment, and let me know how you use it. I always love hearing different use cases.

PowerShell: Working with CSV Files

PowerShell: Working with CSV Files

Why did I want to do this?

I wanted to learn how to manipulate CSV files for logging purposes as well as for part of a script I was writing to email out a different quest each day. 

In particular I wanted to see if today was completed, and if not, update it to say so and then add the next day with Completed = No.

The setup

This function is part of a larger script that I'll write a post about after covering more of the basics.

You'll need the following things to get it running:

A file named runlog.csv in the same folder with the following contents:

“DateTime”,”Day”,”Completed”
”1/18/2016”,”1”,”No”

Preferably you'll want the first DateTime to match the current date, and day to start at 1. The script should be able to catch a gap in the DateTime and continue on, however.

Now you'll need to setup the following variables:

$todayDate        = (Get-Date).ToShortDateString()
$tomorrowDate     = (Get-Date).AddDays(1).ToShortDateString()
$runLog           = Import-CSV .\runlog.csv
$logHeaders       = @{
    "DateTime"  = '' 
    "Day"       = ''
    "Completed" = '' 
}

How to call the function

You'll want to call the function like so:

if ($updateLog)   {Log-DayQuest $todayDate $tomorrowDate $logHeaders}

At the beginning of the script you'd want something like:

[cmdletbinding()]
Param(
    [boolean]
    $updateLog
)

The code (let's put it all together!)

[cmdletbinding()]
Param(
    [boolean]
    $updateLog
)

$todayDate        = (Get-Date).ToShortDateString()
$tomorrowDate     = (Get-Date).AddDays(1).ToShortDateString()
$runLog           = Import-CSV .\runlog.csv
$logHeaders       = @{
    "DateTime"  = '' 
    "Day"       = ''
    "Completed" = '' 
} 

function Log-DayQuest {
    [cmdletbinding()]
    param($todayDate,$tomorrowDate,$logHeaders)
    
    [int]$questDay   = ($runLog | Where-Object {$_.DateTime -eq $todayDate} | Select-Object Day).Day
    
        if (($runLog | Where-Object {$_.DateTime -eq $todayDate} | Select-Object Completed).Completed -eq "Yes") {
    
            Write-Host "Log already updated!"
 
        } Elseif ($runLog | Where-Object {$_.DateTime -eq $todayDate})  { 

            [int]$day = ($runLog | Where-Object {$_.DateTime -eq $todayDate} | Select-Object Day).Day
        
            #Log today as completed
            ($runLog | Where-Object {$_.DateTime -eq $todayDate}).Completed = "Yes"        

            $runLog | Export-CSV .\runlog.csv -NoTypeInformation
          
            #Log tomorrow as not completed
            
            $logHeaders.DateTime  = $tomorrowDate
            $logheaders.Day       = $day+1
            $logheaders.Completed = "No"

            $newrow = New-Object PSObject -Property $logHeaders
            Export-CSV .\runLog.csv -InputObject $newrow -append -Force
            
            Write-Host "Log updated!"
        
        } elseif($runLog | Where-Object {$_.DateTime -eq $todayDate} -eq $null) {
            
            Write-Host "No entry for today... creating entry and updating"
            [int]$day = ($runlog[$runlog.count-1]).day 
            $logHeaders.DateTime  = $todayDate
            $logheaders.Day       = $day+1
            $logheaders.Completed = "Yes"
            
            $newrow = New-Object PSObject -Property $logHeaders
            Export-CSV .\runLog.csv -InputObject $newrow -append -Force

        }
}

if ($updateLog)   {Log-DayQuest $todayDate $tomorrowDate $logHeaders}

The results

Here are the results of me testing the script.

More on how it works coming up!

Please let me know what you think or if you have a quicker way to accomplish the same thing.

One of the things I love about PowerShell are the different ways to accomplish the same thing. That's the best way to learn.