PowerShell – Prompt/Confirm User’s Choice (A Simple Yes/No function)

Here’s a sample of a custom function inspired from a previous PowerShell Tip of the Week:

function Confirm-UserChoice
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([bool])]
    Param
    (
        [Parameter(Mandatory=$true,
                   Position=0)]
        [Alias('Title')]
        [string] 
        $PromptTitle,
        [Parameter(Mandatory=$true,
                   Position=1)]
        [Alias('Message')] 
        [string]
        $PromptMessage,
        [Parameter(Mandatory=$false,
                   Position=2)]
        [Alias('YesDesc')]
        [string]
        $YesOptionDescription,
        [Parameter(Mandatory=$false,
                   Position=3)]
        [Alias('NoDesc')]
        [string]
        $NoOptionDescription
    )
    Begin
    {
        if ($PSBoundParameters['YesOptionDescription'])
        {
            $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes',$YesOptionDescription
        }
        else
        {
            $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'
        }
        if ($PSBoundParameters['NoOptionDescription'])
        {
            $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No',$NoOptionDescription
        }
        else
        {
            $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No'
        }
        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
        #$maybe = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Maybe','Maybe we want to do provide more options'
        #$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no,$maybe)
    }
    Process
    {
        $result = $Host.UI.PromptForChoice($PromptTitle, $PromptMessage, $options, 0) # Index of default option. '0' in this case represents 'Yes' in the above example, '1' for 'No'.

    switch ($result)
        {
            0 {return $true}
            1 {return $false}
        }
    }
    End
    {
        Remove-Variable -Name yes,no,options,result
    }
}

Where the usage looks like this:

Confirm-UserChoice -PromptTitle 'Confirm Action' -PromptMessage 'Do you really want to do this?' -YesOptionDescription 'Yes, I do.' -NoOptionDescription 'No, I do not.'

Bonus example: a slightly, possibly bad design decision, that let’s you make a mini pop-up selection at once:

function Confirm-OnTheFlyChoice
{
    [CmdletBinding(DefaultParameterSetName='Default', 
                  PositionalBinding=$false,
                  ConfirmImpact='Medium')]
    [Alias('cotfc')]
    Param
    (
        [Parameter(Mandatory=$true, 
                   Position=0,
                   ParameterSetName='Default')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias('Title')] 
        [string]
        $PromptTitle,
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   Position=1,
                   ParameterSetName='Default')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias('Message')] 
        [string]
        $PromptMessage,
        [Parameter(Mandatory=$true,
                   Position=2,
                   HelpMessage='Menu options:',
                   ParameterSetName='Default')]
        [string[]]
        $Option
    )
    Begin
    {
        $OptionArray = New-Object -TypeName System.Collections.ArrayList
        Write-Verbose -Message 'Listing Parameters utilized:'
        $PSBoundParameters.GetEnumerator() | ForEach-Object -Process { Write-Verbose -Message "$($PSItem)" }
    } # END: Begin
    Process
    {
        for ($i = 0; $i -lt $Option.Count; $i++)
        { 
            $OptionArray.Add("&$($Option[$i])") | Out-Null
        }
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($OptionArray)
	        $result = $Host.UI.PromptForChoice($PromptTitle, $PromptMessage, $options, 0)
    } # END: Process
    End
    {
        return $Option[$result]
        Remove-Variable -Name OptionArray,options,result
    } # END: End
}

Where the usage would be:

Confirm-OnTheFlyChoice -PromptTitle 'I needed a fast menu' -PromptMessage 'What do you select?' -Option Bird,Cat,Dog,Fish

Note on a bug: the Confirm-OnTheFlyChoice would introduce an issue as-is if you have two values with the same starting letter.
Example:

-Option Fish,Ferret
Advertisements

Windows Anniversary – Enable Bash

Hurrah! Windows 10 (Version 10.0.14393) is here.

Prerequisites: you will need a 64-bit installation of Windows. Then you will need to have enable Developer Mode .

Start a new PowerShell window running as an Administrator. Run the following to enable the feature:

Enable-WindowsOptionalFeature -Online -FeatureName 'Microsoft-Windows-Subsystem-Linux'

Reboot your device and log back in.
Open the Start Menu, and type in “bash”.  (It points here: C:\Windows\System32\bash.exe).

The following prompt will appear:

-- Beta feature --
This will install Ubuntu on Windows, distributed by Canonical
and licensed under its terms available here:
https://aka.ms/uowterms
Type "y" to continue:

If you agree, type in “y” and press <Enter>. A download will begin:

Downloading from the Windows Store... 100%
Extracting filesystem, this will take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: <username>
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Installation successful!
The environment will start momentarily...
Documentation is available at:  https://aka.ms/wsldocs
<username>@<hostname>:/mnt/c/Windows/System32$

Then go to town! You’ll get a new shortcut labeled ‘Bash on Ubuntu on Windows’ which points at the bash.exe and get the initial package updates out of the way.

<username>@<hostname>:/mnt/c/Windows/System32$ sudo apt-get update
<username>@<hostname>:/mnt/c/Windows/System32$ sudo apt-get upgrade

And does ‘cmatrix’ work…?

<username>@<hostname>:/mnt/c/Windows/System32$ sudo apt-get install cmatrix cmatrix-xfont
<username>@<hostname>:/mnt/c/Windows/System32$ cmatrix

It does!

If you want to poke around from Windows, the root filesystem is located here:

%USERPROFILE%\AppData\Local\Lxss\rootfs

PowerShell – Passing An Argument List Within A Script Block


$Options = @{'Variable'='test'}

Invoke-Command -ArgumentList $Options -ScriptBlock {
Param($Options)&{
Param(
[string]
$Variable
)
Write-Output -InputObject "ComputerName: $env:COMPUTERNAME"
Write-Output -InputObject "Variable: $Variable"
} @Options
} # End: Script block

# Sample Output:
# ComputerName: MY-LOCALHOST
# Variable: test

Thanks to this post:
http://stackoverflow.com/questions/28234509/powershell-splatting-the-argumentlist-on-invoke-command

PowerShell – Break vs. Continue Usage

For this example, we create two ArrayList objects and add a couple ints to them to run a loop to see if the value matches between the two and either continues a loop or breaks out of a loop:

$arraylist1 = New-Object -TypeName System.Collections.ArrayList
$arraylist1.Add(1) | Out-Null
$arraylist1.Add(2) | Out-Null
$arraylist1.Add(3) | Out-Null
$arraylist2 = New-Object -TypeName System.Collections.ArrayList
$arraylist2.Add(1) | Out-Null
$arraylist2.Add(2) | Out-Null
$arraylist2.Add(3) | Out-Null
Sample code for ‘continue’:
foreach ($item in $arraylist1)
{
    Write-Output -InputObject "item is: $item"
    foreach ($subitem in $arraylist2)
    {
        Write-Output -InputObject "subitem is: $subitem"
        if ($item -eq $subitem)
        {
            Write-Output -InputObject "$item matches $subitem! Continuing!"
            continue
        }
    }
}

…Which outputs:

item is: 1
subitem is: 1
1 matches 1! Continuing!
subitem is: 2
subitem is: 3
item is: 2
subitem is: 1
subitem is: 2
2 matches 2! Continuing!
subitem is: 3
item is: 3
subitem is: 1
subitem is: 2
subitem is: 3
3 matches 3! Continuing!
Sample code for ‘break’:
foreach ($item in $arraylist1)
{
    Write-Output -InputObject "item is: $item"
    foreach ($subitem in $arraylist2)
    {
        Write-Output -InputObject "subitem is: $subitem"
        if ($item -eq $subitem)
        {
            Write-Output -InputObject "$item matches $subitem! Breaking!"
            break
        }
    }
}

…which outputs:

item is: 1
subitem is: 1
1 matches 1! Breaking!
item is: 2
subitem is: 1
subitem is: 2
2 matches 2! Breaking!
item is: 3
subitem is: 1
subitem is: 2
subitem is: 3
3 matches 3! Breaking!

What can you use this for or when should I use what one? It depends on your use case. 🙂

C#, XAML, and System.Management.Automation Reference

Do you get an error attempting to create a XAML app referencing System.Management.Automation (which you need for PowerShell much like is described as in this link)? You also tried adding it in via the Add>Reference… option from within the Solution Explorer which only seems to have System.Management?

It displays something like this:
XAML - Using directive is unnecessary
How to fix:

Install the NuGet package and reference it in your project.

Alternatively:
1 – Save your XAML Project and close it.
2 – Using your favorite text editor, open the <MyXAMLApp>.csproj file and add the reference manually with:

<Reference Include=”System.Management.Automation” />

XAML - Editing XAMLApp.csproj
3 – Save and re-open your project. Boom! You can now create new instances:
PowerShell ps = PowerShell.Create();

PowerShell – Calculated Property Example & Export-Csv Formatting


Import-Module MSOnline
Connect-MSOLService -Credential (Get-Credential)
$groupid = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' # A GroupID with a known single Error.
$groupdetails = Get-MSOLGroup -ObjectID $groupid -ErrorAction 'Stop'
$groupdetails | `
Select-Object ObjectID,DisplayName,GroupType,`
@{ Label='ExpandedErrorCode';Expression={$PSItem.Errors[0].ErrorDetail.ObjectErrors.ErrorRecord.ErrorCode} },`
@{ Label='ExpandedErrorDescription';Expression={$PSItem.Errors[0].ErrorDetail.ObjectErrors.ErrorRecord.ErrorDescription} }
| `
Export-Csv -LiteralFile "$env:USERPROFILE\Desktop\MyGroupFile.csv" -NoTypeInformation
Remove-Variable groupid

Running $groupdetails | Get-Member shows Name of ‘Errors’ as a MemberType of: ‘Property’, with a Definition of: ‘System.Collections.Generic.List[Microsoft.Online.Administration.ValidationError] Errors {get;set;} ‘