Monthly Archives: January 2015

What PowerShell Modules does the RSAT Provide (on Windows 8.1)

I built a new Windows 8.1 machine recently, and as we all know, doing that requires new software installations. In my case, that included the RSAT. The RSAT, or Remote Server Administration Tools, allow IT admins the ability to remotely manage features on Windows Server operating systems from a client operating system—here’s a link to the RSAT for Windows 8.1.

Prior to running the RSAT installer, I wanted to collect the currently available Windows PowerShell modules that I already had on my computer. This would allow me to know exactly what modules were added after the RSAT installer finished. The command below collects all the modules, returns only their name, and then drops that into a file on my computer. We’ll use the file in a moment, as part of a comparison.

PS C:\> Get-Module -ListAvailable | Select-Object -ExpandProperty Name | Out-File C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt

I verified the file contained the module names by using the Get-Content cmdlet. In addition, I ran a slightly modified command to get the item count in the file—both can be seen below. The output created by the first command has been truncated to save space.

PS C:\> Get-Content C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt
AppBackgroundTask
AppLocker
Appx
AssignedAccess
...
PS C:\> (Get-Content C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt).Count
57

Once this file was in placed, and I was satisfied that it contained what I wanted, I went ahead with the RSAT install. Upon completion, I reran the Get-Module command above, after modifying the name of the file it would create (pre vs. post). I then read in the contents of the new file (which has been truncated again), and checked the number of modules listed in the file. There were now 75 modules where there had only been 57 before the RSAT install.

PS C:\> Get-Module -ListAvailable | Select-Object -ExpandProperty Name | Out-File C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt
PS C:\> Get-Content C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt
ActiveDirectory
AppBackgroundTask
AppLocker
Appx
...
PS C:\> (Get-Content C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt).Count
75

Knowing that there’s 18 new modules is helpful, but which ones were added? The Compare-Object cmdlet can tell us. As seen below, we have the cmdlet read in the lines of each file and then determine which ones aren’t included in both files.

PS C:\> Compare-Object -ReferenceObject (Get-Content C:\Users\tommymaynard\Desktop\Pre-RSAT-PowerShell-Modules.txt) -DifferenceObject (Get-Content C:\Users\tommymaynard\Desktop\Post-RSAT-PowerShell-Modules.txt)

InputObject                                                 SideIndicator
-----------                                                 -------------
ActiveDirectory                                             =>
BestPractices                                               =>
ClusterAwareUpdating                                        =>
DFSN                                                        =>
DFSR                                                        =>
DhcpServer                                                  =>
DnsServer                                                   =>
FailoverClusters                                            =>
GroupPolicy                                                 =>
IpamServer                                                  =>
IscsiTarget                                                 =>
NetworkLoadBalancingClusters                                =>
NFS                                                         =>
RemoteAccess                                                =>
RemoteDesktop                                               =>
ServerManager                                               =>
ServerManagerTasks                                          =>
UpdateServices                                              =>

As we can see above, our Compare-Object results indicate that the difference object (the post file, or the file on the right) has new modules—now we know which ones were added since installing the RSAT.

Using the Range Operator for Calculating Total Push-Ups

In December of 2014, I decided that my life in 2015 was in need of some push-ups. Instead of just starting with 10 a day, or some other arbitrary number, I thought I would do as many push-ups a day as it was the day in the year. This meant that on day one (January 1, 2015), I would do one push-up and on day two, I would do two push-ups, and so on. Today is the 20th day of the new year, and so I’ll have to do 20 tonight. I wanted to know how many push-ups I will have done by January 31st. Being the Windows PowerShell hobbyist that I am, I enlisted PowerShell to do my calculations for me.

I started with a variable, $EndDay, and the range operator (..). The combination of the two provides me an integer array of the days in January, such as 1..$EndDay (or, 1..31). Using this, I can calculate how many total push-ups I will have done by the end of the day on January 31st. The example below sets up the integer array, as well as the ForEach-Object loop where we’ll do our calculations. Note: I’m using the ForEach-Object alias, foreach.

$EndDay = 31
1..$EndDay | foreach {

}

The first thing we do, below, is include a second variable, $PushUps, that will collect the total number of push-ups for the month. We’ll use the += assignment operator. This operator takes whatever is already in $PushUps, and adds to it. If the current value stored in $PushUps was 1, and we used the += assignment operator like so, $PushUps += 2, then the value in $PushUps would be 3 (1 + 2 is equal to 3). If we used the standard assignment operator (=), then $PushUps would be 2, as 1 would be overwritten.

On the next line, below, we write some information on the screen. We write the current day: that’s the current number from the integer array represented by $_ (as of PowerShell 3.0, $_ can be represented as $PSItem). Then we write out the total number of push-ups completed by that day: $PushUps.

$EndDay = 31
1..$EndDay | foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
}

I noticed that when I reran the code in the ISE, that the value of $PushUps was incorrect on the second run. This is because the variable already exists, and by the end of the first run already contains 496—the number of push-ups I’ll have done by the end of January! Therefore, I added an If statement that removed the $PushUps variable when $_ was equal to $EndDay. This happens on the final run through the foreach.

$EndDay = 31
1..$EndDay| foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
    If ($_ -eq $EndDay) {
        Remove-Variable PushUps
    }
}

If you change the value for $EndDay to 365, you’ll be able to determine that after December 31st (if I can somehow keep this up) I will have done 66,299 total push-ups for the year. It’s hard to imagine that I could do 365 push-ups at once, but then again, it’s hard to imagine I’ll get though the rest of the month. Here’s an image that shows the the full results when we run the function above.

Using the Range Operator for Push-Up Calculations

Thanks for reading, and wish me good luck—I’m going to need it.

Write-Host, Does it Have a Place?

I’ve been working on an advanced function that I can’t wait to share (and no, it’s not the one in this post). I really think it’s something that the Windows PowerShell community has been missing. Okay fine, maybe it’s just something I’ve been missing.

I noticed in development that my Write-Output messages to the user were crossing the pipeline, when the custom object (created by the function), was passed to Select-Object (in certain manners). I have a function below that does the same thing as the one in development.

Here’s how this thing works: The function requires the user to provide the value Write-Output (or, wo), or Write-Host (or, wh) for the -Option parameter. This will determine how the message to the user is written inside the Begin block. The only other thing that’s happening in this function, is that a custom object is being created in the Process block, based on some properties of Win32_BIOS.

Function Test-TMWriteOutputVsHost {
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true,
        HelpMessage="Enter Write-Output or Write-Host")]
    [ValidateSet('Write-Output','wo','Write-Host','wh')]
    [string]$Option
    )

    Begin {
        If ($Option -eq 'Write-Output' -or $Option -eq 'wo') {
            Write-Output 'Inside the Begin block (using Write-Output).'

        } ElseIf ($Option -eq 'Write-Host' -or $Option -eq 'wh') {
            Write-Host 'Inside the Begin block (using Write-Host).'
        }
    } # End Begin

    Process {
        $CollectionVariable = Get-WmiObject -Class Win32_BIOS
        $Object = @()
        $Object += [pscustomobject]@{
            Manufacturer = $CollectionVariable.Manufacturer;
            Name = $CollectionVariable.Name;
            Version = $CollectionVariable.Version
        }
    } # End Process

    End {
        Write-Output $Object
    }
    # End, End
} # End Function

As we can see below, everything works great with both Write-Output or Write-Host, when we don’t pipe the function to the Select-Object cmdlet.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output
Inside the Begin block (using Write-Output).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

Now, let’s pipe our object to some variations of the Select-Object cmdlet and watch some things blow up (when using Write-Output).

PS C:\> # Use the horizontal scrollbar to see the results...
PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select *

                                                                                                                 Length
                                                                                                                 ------
                                                                                                                     44

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select Name,Ver*

Name                                                        Ver*
----                                                        ----

BIOS Date: 08/27/13 11:12:44 Ver: A13.00

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select *
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select Name,Ver*
Inside the Begin block (using Write-Host).

Name                                                        Version
----                                                        -------
BIOS Date: 08/27/13 11:12:44 Ver: A13.00                    DELL   - 1072009

The problem with Write-Output, is that the object it’s producing is crossing our pipeline and causing unpredictable behavior—something we can’t include in a function we want to distribute. Here’s the proof: When we pipe our function to Get-Member, we reveal the objects that show up on the other side, and we don’t want the string object coming over with us.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.String
System.Management.Automation.PSCustomObject

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | Get-Member | Select-Object TypeName -Unique
Inside the Begin block (using Write-Host).

TypeName
--------
System.Management.Automation.PSCustomObject

I don’t profess to know it all, so if there’s a way to get around this using Write-Output, then I’d love to hear about it. While I haven’t tried it, I suspect I may be able to create an embedded function—that might be the trick I need. Perhaps I’ll play with that option at another time. Thanks for reading!

Oh, and before someone mentions it, I explained how I feel about Write-Verbose in the comments on a post by Adam Bertram: http://www.adamtheautomator.com/use-write-host-lot.

Out-GridView in a PSRemoting Session

Twitter Reply to:

We can’t use the Out-GridView cmdlet in a remote session. How do we know this? Well for one, the documentation says so (search for ‘You cannot use a remote command’ on that webpage), and two, because if you try, you’ll get a straight forward error message on the topic: “Out-GridView does not work in a remote session.” Okay, but why?

Out-GridView produces a Graphic User Interface (GUI) — something we don’t use in PSRemoting sessions. In fact, everything about a PSRemoting session is text only. This isn’t just about Out-GridView, though. Other graphical elements in Windows PowerShell aren’t going to work either. This includes Get-Help’s -ShowWindow parameter, and the Show-Command cmdlet. It just wasn’t designed to work this way.

Other GUI elements don’t work either. While you won’t get a helpful message, like you do with the PowerShell cmdlets and parameters, launching notepad.exe and calc.exe isn’t going to work like it does on a local computer. Those programs work a little differently though, as they will actually launch on the remote computer. It just won’t be in any useable fashion from your remote session. Bonus: They may lock up your remote session until they are closed.

Note: If you were to try this, one way to rectify the situation would be to open a second PowerShell console and run the following, assuming you launched calc.exe: Invoke-Command -ComputerName computername -ScriptBlock {Get-Process -Name calc.exe | Stop-Process}. This would end the process on the remote computer, and give you back your prompt on the console where you were running your PSRemoting session.

Everything until now, assumed we were talking about an interactive PSRemoting session (using the Enter-PSSession cmdlet). Well, what about Invoke-Command? Invoke-Command is used to run commands on remote systems and return the results to the local computer. As you can see in the examples below, we can run a command on a remote computer and then display the results on our local computer, inside Out-GridView.

Note: Although I didn’t have any problems with the small handful of cmdlets I tried, the same webpage linked above indicates that data returned from a remote computer may not be formatted correctly for use with Out-GridView.

PS C:\> $Services = Invoke-Command -ComputerName dc01 -ScriptBlock {Get-Service}
PS C:\> $Services | Out-GridView
PS C:\> $PSWARules = Invoke-Command -ComputerName PSWAServer01 -ScriptBlock {Get-PSWAAuthorizationRule}
PS C:\> $PSWARules | Out-GridView

Thanks for the inspiration to write a little about this topic, Tim.

Creating Multiple Credential Objects

Download the complete function: https://gist.github.com/tommymaynard/98031ccd5de67005bf3063db06a33851

There are times when you made need to use additional credentials, other than those used to begin the Windows PowerShell session. When I need to PSRemote to another domain’s computer, I quickly run a function I have stored in my $PROFILE to create a variable that contains a credential object for the second domain. It’s a bit more specific for my environment, so I won’t bother sharing that exact function. What I will do, however, is share and explain a function I’ve written to create up to 10 credential objects. You’ll soon see where that can be changed (if for some reason someone would want more than that many). Realistically, 10 seems much too high anyway. Moving on.

Now, it might be important to know a bit more about how this started. The unfortunate thing about that function (the one in the link) is that it is maxed out at three credential sets (and it continually used the word ‘domain’). As well, it wasn’t as flexible as it should’ve been and it didn’t have any comment-based help or any verbose statements. So, a couple of days after publishing that post, I copied the function back into the PowerShell ISE and started working on a “1.1” version. That’s what we’ll discuss in this post.

First, we’ll write some basic, structural code for the advanced function.

Function New-TMMultiCred {
    [CmdletBinding()]
    Param ()

    Begin {
    } #End Begin

    Process {
    } #End Begin
} #End Function

Now, let’s add the parameter that will define how many credential objects the function will create. The variable we’ll use is $Set and we’ll cast it as an integer (Set will, therefore, also be the name of the parameter). In addition, we’ll add code to define the -Set parameter as being mandatory (it must be included when the function is run), and make the parameter positional (the value for -Set can be entered without providing the -Set parameter name). In addition, we’ll add the ValidateRange validation attribute that will require that the integer entered, as the value for the -Set parameter, must be 1 through 10. This can be changed if necessary.

Function New-TMMultiCred {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateRange(1,10)]
        [int]$Set
    )

    Begin {
    } #End Begin

    Process {
    } #End Begin
} #End Function

We won’t need to do anything in the Begin block, so we’ll focus on the Process block next. In there, we’ll need to prompt for a user name and password as many times as the value of the parameter -Set indicates. Since we know the number of times we’ll be looping (to prompt for the username and password), I recommend we use a for statement. For statements work this way: set a variable ($i in our case) as a counter variable, add a comparison to determine how many times to loop (while $i is less than or equal to $Set), and finally, include a way to increment the counter variable (that’s what $i++ does), so that we only loop the proper number of times.

Function New-TMMultiCred {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateRange(1,10)]
        [int]$Set
    )

    Begin {
    } #End Begin

    Process {
        For ($i = 1; $i -le $Set; $i++) {

        }
    } #End Begin
} #End Function

The final piece is adding the parts necessary to prompt for usernames and passwords, and creating variables to store each of the credential objects. We’ll do this as part of a try-catch.

Line 15 below, first runs the Get-Credential cmdlet. We know this because it is in parenthesis. These parentheses indicate that this cmdlet needs to run before it’s used as the value of the Set-Variable‘s -Value parameter. If for some reason the user presses Cancel, or presses the X in the top-right corner of the prompt dialog, the try portion of the try-catch will fail, and the catch portion will run. It will indicate that no credential was created for that iteration through the loop.

If the user enters, at minimum a username (because a password can be blank), then it will set a variable called $CredSet# (the hash mark (#) indicates a number). If we indicate we want to create two credential objects when we run the function (New-TMMultiCred 2), then $CredSet1 will be the variable that holds the first credential object, and $CredSet2 will hold the second.

Still on line 15, notice that the -Scope parameter is being used with the Set-Variable cmdlet. If we didn’t include this parameter and its value, Global, then the variables created (or modified, if the variable(s) already existed) by this command would not be available after the function was done executing.

Function New-TMMultiCred {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateRange(1,10)]
        [int]$Set
    )

    Begin {
    } #End Begin

    Process {
        For ($i = 1; $i -le $Set; $i++) {
            try {
                Set-Variable -Name CredSet$($i) -Value (Get-Credential -Credential $null) -Scope Global
                Write-Output -Verbose "SUCCESS: Credential Set $i stored in `$CredSet$($i)"
            } catch {
                Write-Warning -Verbose "No credential object was created for set $($i)."
            }
        }
    } #End Begin
} #End Function

Here’s a look at the function in progress. The first image shows that three credential objects were requested. The first credential object has already been created and is stored in $CredSet1, the second wasn’t created, since the user pressed Cancel on the second prompt, and the third credential object will be created when the user presses OK. The second image shows the end result.

Function for Creating Multiple Credential Objects-01

Function for Creating Multiple Credential Objects-02

Once this is complete, the user can run cmdlets that have an optional -Credential parameter and supply the fitting credential object, as seen in the example below. You can return all the cmdlets and functions that have the -Credential parameter using Get-Command: Get-Command -ParameterName Credential.

PS C:\> Invoke-Command -ComputerName dc01 -ScriptBlock {Get-Date} -Credential $CredSet3

Download the complete function: https://gist.github.com/tommymaynard/98031ccd5de67005bf3063db06a33851

Three Sets of Credentials for Three (or the Same) Domains

I briefly scanned this topic on Reddit’s PowerShell subreddit. I’m not really sure if this will help anyone, but the first thing I thought of was to create an advanced function that could prompt for up to three different sets of credentials, each for a different domain. It’s loosely based on a function I have in my $PROFILE that I use for a second domain. It should be noted that you don’t have to use the function for different domains. It can be used for different user accounts in the same domain—that part is up to you.

Function New-TMMultiDomainCred {
    [CmdletBinding()]
    Param (
        [switch]$Domain1,
        [switch]$Domain2,
        [switch]$Domain3
    )

    Begin {
    } #End Begin

    Process {
        Switch ($PSBoundParameters) {
            {$PSBoundParameters.Keys -contains 'Domain1'} {
                $Global:Domain1Cred = Get-Credential -Credential $null
                Write-Output -Verbose 'Domain Creds for Domain1 stored in $Domain1Cred'
            }
            {$PSBoundParameters.Keys -contains 'Domain2'} {
                $Global:Domain2Cred = Get-Credential -Credential $null
                Write-Output -Verbose 'Domain Creds for Domain2 stored in $Domain2Cred'
            }
            {$PSBoundParameters.Keys -contains 'Domain3'} {
                $Global:Domain3Cred = Get-Credential -Credential $null
                Write-Output -Verbose 'Domain Creds for Domain3 stored in $Domain3Cred'
            }
            default {
                Write-Warning -Verbose 'Parameters must be provided to use this advanced function.'
            }
        }
    } #End Process
} #End Function

Checking a Variable for a Path

I helped someone on a forum recently that needed to determine if a path existed or not. Providing the path did exist, they would need to perform an action. The problem wasn’t what to do (in the end), it was determining if the path existed (to begin with). Here’s a rough draft of their code:

$Path = 'C:\Scripts\TestFolder'

If ($Path) {
    Remove-Item -Path $Path
}

If you’ve been doing this a little while, then it’s quite easy to spot the problem with the “logic” used in the example above. The If statement’s condition is only evaluating if $Path has a value (if it contains something). The fact that the value may, or may not, be a path, and whether or not it exists (if it is a path), is not being evaluated. I recommended they use the Test-Path cmdlet, to ensure the value stored in $Path, was a path, and that it existed.

$Path = 'C:\Scripts\TestFolder'

If (Test-Path -Path $Path) {
    Remove-Item -Path $Path
}

There are times when all you will want to know is if a variable contains a value. As an example, we can use a variable to record if a specific process is running (at the time the variable is created), and take action depending on if it is, or isn’t.

The example below checks for a process called pn (Programmer’s Notepad) and stores the results in the $PNProcess variable. We can then check and determine if $PNProcess has been set, or assigned a value, and make a determination of what to do based on the results. Since we’re only looking to see if our variable contains a value, then this is a perfectly suitable solution.

$PNProcess = Get-Process -ProcessName pn -ErrorAction SilentlyContinue

If ($PNProcess) {
    Write-Output -Verbose "Programmer's Notepad is running."
} Else {
    Write-Output -Verbose "Programmer's Notepad is not running."
}

Note: While I would normally wrap a string in single quotes, double quotes are being used because single quotes will not work when there’s a single quote in the string.