Try default values in PowerShell parameters for flexibility (original) (raw)

Highly customized PowerShell code works great, until it hits a brick wall.

When you learn to write PowerShell functions, you might start by solving a specific problem. As you use the function and gain more scripting experience, you'll see ways the function would be more useful if it had the versatility to solve the same problem, but in a slightly differently way.

Learning how to use default values in parameters makes your code both able to adapt to different situations and simpler to understand.

Using default values widens a parameter's usefulness

Let's say you have a VPN set up on your Windows-based client or server. You want a function to connect to that VPN with a specific set of credentials stored in the local Windows Credential Manager.

This is what that function might look like:

Function Connect-WorkVpn {
    $cred = Get-StoredCredential -Target 'Work VPN'
    $vpn = Get-VpnConnection -Name 'Work'
    if ($vpn.ConnectionStatus -ne 'Connected') {
        rasdial.exe (((vpn.name) (((cred.username) (((cred.GetNetworkCredential().password)
    }
}

The Get-StoredCredential cmdlet is part of the CredentialManager module, which creates and retrieves credentials stored in the Windows Credential Manager. In this scenario, the cmdlet avoids the need to type the username and password every time you connect, but you can still store the credentials securely. The Target parameter is the name of the credential to be retrieved.

The remainder of the function gets the VPN connection using Get-VpnConnection and, if not connected, it connects using the Windows rasdial.exe application.

Connect to the VPN by calling the function:

Connect-WorkVpn

This is a single-purpose function: It connects to the VPN named Work using the Work VPN credentials without accepting any parameters. When you add other VPNs on your system, then you can convert this into a more adaptable function with more features, while utilizing default parameter values to keep the usage simple.

The updated function adds several parameters to account for an environment with multiple VPNs:

Function Connect-Vpn {
    param (
        [string]$VpnName,
        [string]$VpnCredName
    )
    cred=Get−StoredCredential−Targetcred = Get-StoredCredential -Target cred=GetStoredCredentialTargetVpnCredName
    vpn=Get−VpnConnection−Namevpn = Get-VpnConnection -Name vpn=GetVpnConnectionNameVpnName
    if ($vpn.ConnectionStatus -ne 'Connected') {
        rasdial.exe (((vpn.name) (((cred.username) (((cred.GetNetworkCredential().password)
    }
}

The revised function requires you to manually enter the VPN name and credential name to connect to the same VPN as before:

Connect-Vpn -VpnName 'Work' -VpnCredName 'Work VPN'

This updated function connects to any configured VPN with any stored credentials, but it has lost its use-case simplicity. You can add default values to bring this flexibility back. To start, assign a value to those parameters inside the param block. If no value passes to those parameters when calling the function, the script will use the hardcoded default values instead.

Function Connect-Vpn {
    param (
        [string]$VpnName = 'Work',
        [string]$VpnCredName = 'Work VPN'
    )
    cred=Get−StoredCredential−Targetcred = Get-StoredCredential -Target cred=GetStoredCredentialTargetVpnCredName
    vpn=Get−VpnConnection−Namevpn = Get-VpnConnection -Name vpn=GetVpnConnectionNameVpnName
    if ($vpn.ConnectionStatus -ne 'Connected') {
        rasdial.exe (((vpn.name) (((cred.username) (((cred.GetNetworkCredential().password)
    }
}

Like normal variable assignment, the code assigns Work to VpnName and Work VPN to the VpnCredName variables. The script will now connect to the Work VPN when running the Connect-Vpn function by assigning those default values to the parameters in the param block.

To set up an additional VPN and credential set, you could connect to another VPN by passing in the correct parameters:

Connect-Vpn -VpnName 'Site1' -VpnCredName 'Site1 VPN'

A default value helps handle remote and local connections

Another useful PowerShell technique is to write a function that works on local and remote computers. To capture the machine name, we can write a function that uses a ComputerName parameter. The script is flexible because the ComputerName parameter defaults to the computer where the function runs.

In this example, we want to comb through the event logs and return all events that are failed login attempts. The events appear in the security log with an ID of 4648. The following script uses a function that does not accept parameters and returns 4648 events from the local computer.

Function Get-ExplicitLogins {
    Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        Id = 4648
    }
}

Like the previous example, we can make the function more useful with a parameter for the computer name and give that a default value. Unless you need the script to check a specific system's logs, use the local computer's name as the default value.

Function Get-ExplicitLogins {
    param (
        [string]$ComputerName = $env:COMPUTERNAME
    )
    Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{
        LogName = 'Security'
        Id = 4648
    }
}

You can call the Get-ExplicitLogins function without any parameters and have it return all the explicit logins for the local computer.

You can pass the function to a computer name to have it return the events from a remote computer:

Get-ExplicitLogins -ComputerName 'Test-PC01'