Tag Archives: Get-PSSession

PowerShell and the LAPS Windows PowerShell Module

There’s a difference between Windows PowerShell and just PowerShell. You know that, right? You know what the difference is, yeah? If not, then do know that Jeff Hicks wrote a great blog post about it recently titled “What the Shell is Happening?” I’m going to assume you’ve read this as I go along, so you might just do that quickly if you haven’t already.

Windows PowerShell and PowerShell really are two different things. We’re moving into a territory where you’ll be able to know how much someone knows about Windows PowerShell/PowerShell based on how they are discussed. It’s going to be like PowerShell quoting rules: You can tell a good deal about someone’s Windows PowerShell/PowerShell knowledge based on when they do and don’t use single and double-quotes.

I was recently looking at some documentation when I happened upon the LAPS Windows PowerShell module. If you’re looking for a strangely named module then look no further; it’s AdmPwd.PS. Seriously. How about just calling it LAPS!? Up until that point in the documentation review I was doing, everything I tried was working in PowerShell — commands from the Hyper-V module, commands from the ActiveDirectory module (read the Edit section at the bottom of this post when done reading this post), and all of the we-ship-these-with-the-product Microsoft modules and commands.

The LAPS module — we’ll just call it that — is stored not only as a Windows PowerShell module, but it’s stored in the C:\Windows folder. That’s C:\Windows\System32\WindowsPowerShell\v1.0\Modules to be exact. While tab-completion didn’t work on the module’s name, the Import-Module cmdlet did work, but not without a warning.

The warning message above has to do with how PSRemoting sessions work. First off, a PSRemoting session is usually done from one computer to another. In this instance, however, it’s doing a PSRemoting session from and to the same computer: mine. In that remoting session, it’s loading up Windows PowerShell (powershell.exe [not pwsh.exe]). As can be seen below, running Get-PSSession will provide information about the Windows PowerShell (or WinPSCompatSession) PSRemoting session. Notice the -1 value for the IdleTimeout. Without any reading and research, I’m going to say that it means the session doesn’t timeout. I left mine up for an hour or so, and sure enough, the LAPS commands continued to work without having to recreate a PSRemoting session.

In any PSRemoting session, whether to the same computer or another, we get deserialized objects as the results. That means that the final results, that end up on the source computer from the destination computer, do not consist of live PowerShell objects. I’m sure you can read more about it, but the result/output is serialized into XML, sent across the wire from one computer to the other, and then deserialized. This has to do with how to quickly move data from one machine to another. In our case, even though it’s the same computer, it’s still making use of the network card and serialization/deserialization. It’s still doing this even if your PSRemoting session is to and from the same computer.

But there’s another way we can use this module. As mentioned in our warning message, Import-Module includes a SkipEditionCheck switch parameter. According to the documentation, this forces the command to skip evaluating CompatiblePSEditions in a module’s manifest file. This is a key within the file that indicates whether or not a module can be used for Windows PowerShell, which uses the term “Desktop” if it can, or PowerShell, which uses the term “Core” if it can. If a module was designed to work with both Windows PowerShell and PowerShell, it would include both terms. The LAPS module was written before CompatiblePSEditions was added to the module manifest files (.psd1 files).

When this parameter is used, it doesn’t look for CompatiblePSEditions, as stated, and appears to load the LAPS module properly. Well, properly enough that I was able to test the Get-AdmPwdPassword command. Before we see some proof supporting that claim, let’s take a look at some very important information from the Import-Module documentation.

It turns out it’s very likely this module and its related commands wouldn’t have worked using the SkipEditionCheck parameter, but so far they do. Oh, I didn’t mention this, but in the documentation for Import-Module and the SkipEditionCheck parameter, it does mention the path where the LAPS module is located, “Allows loading a module from the “$($env:windir)\System32\WindowsPowerShell\v1.0\Modules” module directory into PowerShell Core when that module does not specify Core in the CompatiblePSEditions manifest field.” So, a part of how this works is also due to the module’s location in the file system. Now, here’s our successful invocation of the Get-AdmPwdPassword command.

By using this method — and we’re lucky we can it seems — we skip creating a PSRemoting session from/to the same computer. Therefore, we aren’t forced to work with deserialized objects. Typically, it’s fine if you do, but with the LAPS module and PowerShell, it’s not a requirement. Take a look at the TypeName value above. That’s a clear indication that we are working with live PowerShell objects. We didn’t see this earlier in our PSRemoting session, but if we had, it would’ve said, “Deserialized.AdmPwd.PSTypes.PasswordInfo.”

Edit: A few days have passed since I first published this post. I didn’t know it then, but I do now! The ActiveDirectory module in PowerShell also creates a PSRemoting session! It looks like it’s running in PowerShell, but it’s really running in Windows PowerShell “behind the scenes.” I’ve had no problems dealing with a deserialized objects, by the way. This is because any piping I might do happens on the remote computer (my computer, but yeah), before the serialization/deserialization process.

Implicit Remoting and the Exchange Cmdlets

I work in an environment where we administer Microsoft Exchange 2010 and I’m proud to say that I don’t have the Exchange Management Tools installed on my laptop. Now, that doesn’t mean I don’t sometimes RDP into an Exchange box, but it does force me to forgo that and use Windows PowerShell when I want to do something quicker than to RDP, log on, and open the EMC. One of the first things I added to my $PROFILE, after writing it for use in a script, was a function that would establish a PSSession to one of the Exchange servers. This allows me to run Exchange-specific cmdlets without leaving my laptop and without having the Exchange Management Tools installed.

The first thing I started with was an empty function that I called New-ExchangeSession.

Function New-ExchangeSession {

}

I had a few requirements for this project. One, I didn’t want to rely on connecting to the same Exchange server each time I created a PSSession. If for some reason it wasn’t available, I would have a problem connecting… in addition to a non-responsive Exchange server. Two, I didn’t want to hard code my Exchange server list in either my script (bad!) or in an external file (not as bad, but not great). What I did was make use of the Get-ADGroupMember and the Get-Random cmdlets to return the names of the Exchange Servers from an Active Directory group in which they were all members, and then randomly select one.

I did this as part of Do-Until loop. In line 3, the first thing it does is acquire the members of the group, ExchangeServers, using the Get-ADGroupMember cmdlet included in the Active Directory module. If you’re running PowerShell 3.0 or greater it will load this module automatically. If you’re not, then you’ll have to add it yourself using the Import-Module cmdlet before beginning the Do-Until loop.

Using dotted notation (.Name) we return only the Name property of the group members. Once collected, the Get-Random cmdlet is used to randomly select one of the names and then assign it to the $ExchServ variable.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-Random (Get-ADGroupMember -Identity 'ExchangeServers').Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)
}

If you’re using something older than 3.0, and you really shouldn’t be, you’ll find the dotted notation version just doesn’t work as expected. In that case, you will have to handle this in a more procedural way as seen in the example below. Regardless of which way you get a server name and assign it to the $ExchServ variable, it must reply to a ping, by use of the Test-Connection cmdlet in line 5 above, and line 7 below. This is the conditional check of Do-Until loop. If Test-Connection returns True, it will break out of the Do-Until. If it returns False, it will randomly select another server and try again.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-ADGroupMember -Identity 'ExchangeServers'
        $ExchServ = Get-Random $ExchServ
        $ExchServ = $ExchServ.Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)
}

We start to create the PSSession once we have an Exchange server name chosen and verified it is reachable across the network. This is done by creating a variable, $Session on line 7 below, that will store the session information. We then use that session information as part of our Import-PSSession cmdlet that brings the Exchange cmdlets down to our local computer. The final, informational message on line 9 simply indicates the Exchange server in which we’ve connected.

Function New-ExchangeSession {
    Do {
        $ExchServ = Get-Random (Get-ADGroupMember -Identity 'ExchangeServers').Name
    }
    Until (Test-Connection -ComputerName $ExchServ -Count 1 -Quiet)

    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchServ.mydomain.com/powershell" -Authentication Kerberos
    Import-PSSession -Session $Session -CommandName * -FormatTypeName * | Out-Null
    Write-Output "Connected to $ExchServ"
}

You will often, if not always, receive a warning about some of the imported commands may have unapproved verbs. To view your active session, use the Get-PSSession cmdlet, and to close the session, use the Remove-PSSession -Id  #, where # equals the Id number returned from the Get-PSSession cmdlet.