Monthly Archives: August 2016

PSMonday #18: Monday, August 29, 2016

Topic: Get-Member

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

In a previous PowerShell Monday — before the seven consecutive weeks of PowerShell remoting — we concatenated the strings ‘5’ and ‘5’ (as ‘5’ + ‘5’) to return ’55’. Additionally, we added the numeric values 5 and 5 (as 5 + 5) to return 10. The problem is that without the command that created the result, we don’t know which result is a string, and which is a numeric value.

55

10

If you’re not using the Get-Member cmdlet, then it’s time you start. The Get-Member cmdlet returns the members of an object. It’s important to know that nearly everything in PowerShell is an object. It’s not terribly difficult to understand, and it’s my belief that Get-Member is one of the three most important, introductory PowerShell cmdlets.

Here’s the thing about objects: They have members. Two of the most common members are properties and methods. Properties are attributes, or things that describe the object. If a person was an object, then the properties might be height, hair color, and toe count. Methods are things the object can do. If a person was an object, then the methods might be jump, run, sleep, and swim. I’ve read about a bicycle being used to explain an object’s properties and methods, too. A bike’s properties might be the chain, the handle bar, and tires. The methods might be brake (or stop), bunny hop, and turn.

Keep this in mind as we further explore objects and the Get-Member cmdlet next week.

PSMonday #17: Monday, August 22, 2016

 Topic: PowerShell Remoting Continued VI

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

If you’re done reading about PowerShell Remoting, then you’ll be glad to know that we’re wrapping it up this week with one final conversation. Today we’ll discuss implicit remoting. I recently read some Microsoft written, PowerShell documentation where they indicated this was an advanced topic. If you’ve been following along over the last several weeks, then as far as I’m concerned, you’re ready. You ought to know a good deal about PS Remoting by now.

Let’s start by considering that we have a PowerShell module called Test on a remote computer called SERVER01. Inside the module are three functions: Write-One, Write-Two, and Write-Three. When invoked, each function will echo the name of the computer where the function is running and then echo either “One!,” “Two!,” or “Three!” Here’s a quick look at the module.

psmonday-17-monday-august-22-2016-01

As we’ve seen before, we can use Enter-PSSession to interactively connect and run any of the functions. We can also use Invoke-Command to run the functions on the remote computer and then return the results. In the implicit remoting option, we’ll create a new PSSession to a remote computer, such as we did last week, and then bring the functions to our local computer.

$Session = New-PSSession -ComputerName SERVER01
Import-PSSession -Session $Session -Module Test

ModuleType Version Name             ExportedCommands
---------- ------- ----             ----------------
Script     1.0     tmp_jfhp0hhd.hk5 {Write-One, Write-Three, Write-Two}

By default, Import-PSSession will return output to include the ModuleType, the version, the local, temporary name it’s using for the module, and the exported commands as seen in the above results. These exported commands are the functions in the module, as you can easily tell. To hide this output, you can pipe the Import-PSSession command to Out-Null, as seen in the below example. It’ll do the same work, but without the output.

Import-PSSession -Session $Session -Module Test | Out-Null

While the temporary module now resides on our computer, it still runs the functions on the remote computer.

Write-One
Write-Two
Write-Three

SERVER01
One!
SERVER01
Two!
SERVER01
Three!

Well, there we go. I think we’ve covered a fair amount of information on PowerShell Remoting. We’ll pick up with something new next week, but don’t be surprised if upcoming topics include PS Remoting in one way, or another. It’s a widely used option, when using PowerShell for remote management.

Windows PowerShell, is PowerShell

In every post I’ve written here, I’ve always made a conscious effort to use the full term “Windows PowerShell,” before I use “PowerShell” later in the post. Well, those times have changed. With the recent news, dropped yesterday (August 18, 2016), PowerShell is open-source and cross-platform. Windows PowerShell is just PowerShell, and it’s available on Linux and Mac OS.

Here’s one of several stories on yesterday’s news: https://blogs.msdn.microsoft.com/powershell/2016/08/18/powershell-on-linux-and-open-source-2. And here, is the story that mentions the name change: https://blogs.msdn.microsoft.com/powershell/2016/08/17/windows-powershell-is-now-powershell-an-open-source-project-with-linux-support-how-did-we-do-it.

I’ve always been pleased by my site’s tagline: “A Windows PowerShell Resource.” Today, I’ve gone ahead and made the change, too, signifying that I’m ready, as well. There’s isn’t a non-Windows post here, but in time, there may be.

Yesterday, and today.

windows-powershell-is-powershell-2016-01

windows-powershell-is-powershell-2016-02

It’s a new era, now. Everything I’ve read, from people I trust in the PowerShell community, says the same thing. The investment we’ve made in PowerShell will be transferable to these other operating systems. We’re better off, than we were just a day ago.

Handle a Parameter Whether it’s Included or Not

I’m in the process of writing a new tool. It’s basically a wrapper function for Get-ADObject that only returns Active Directory (AD) contact objects. While there’s a Get-ADComputer cmdlet to get AD computer objects, and a Get-ADUser cmdlet to get AD user objects, there’s no specific cmdlet for contacts. There’s no surprise here really, and that’s likely why we have a Get-ADObject cmdlet. It’s for those AD objects that didn’t make the cut and get their own.

I’ve seen some discussion on this in the past: How do I handle running a cmdlet within a function when I don’t know if a parameter and parameter value will be included, or not? Let’s consider that my Get-ADContact function will run the Get-ADObject cmdlet, as seen below. In the Begin block, we create a $Parameter variable that will hold a hash table. We’ll populate it in such a way that the key Filter will have a corresponding value of {ObjectClass -eq ‘contact’}. In the Process block, we splat this hash table, currently with a single parameter and parameter value, to the Get-ADObject cmdlet.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

    End {
    } # End End.
} # End Function: Get-ADContact.

There’s two other parameters included in Get-ADObject that I want to allow my users the ability to include. That’s the -SearchBase and -SearchScope parameters. You can read more by checking the help for Get-ADObject: Get-Help -Name Get-ADObject. There’s actually several AD cmdlets that also include these parameters. They can be quite helpful, so that’s why I’ve decided to include them.

Before we continue, I want to let the audience know that I am familiar with creating proxy functions, and I understand it might’ve been a better option. Maybe I’ll circle back sometime and see about replicating the functionality I’ve created here, in that manner. It might turn out this wasn’t worth writing and posting. No guarantee, but it’s possible.

Okay, back on track. Let’s add the additional lines inside the Param block, that make accepting a -SearchBase and -SearchScope parameter value possible.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [string]$SearchBase,

        [Parameter()]
        [ValidateSet(0,'Base',1,'OneLevel',2,'SubTree')]
        $SearchScope
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

    End {
    } # End End.
} # End Function: Get-ADContact.

Now, our Get-ADContact function will include the two additional parameters. Neither parameter is mandatory, but the -SearchScope parameter does include a ValidateSet parameter attribute to ensure it’ll only accept the values 0, 1, 2, Base, OneLevel, or SubTree. Base and 0 are equivalent, as are 1 and OneLevel, and 2 and SubTree.

The next thing I need to do is include the parameter values assigned to the -SearchBase and -SearchScope parameters to our $Parameters hash table when those are included. I decided to do this using the $PSBoundParameters variable, the ForEach-Object cmdlet, and the switch language construct.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [string]$SearchBase,

        [Parameter()]
        [ValidateSet(0,'Base',1,'OneLevel',2,'SubTree')]
        $SearchScope
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}

        $PSBoundParameters.Keys | ForEach-Object {
            Switch ($_) {
                'SearchBase' {$Parameters += @{SearchBase = $SearchBase}; break}
                'SearchScope' {$Parameters += @{SearchScope = $SearchScope}}
            }
        }
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

    End {
    } # End End.
} # End Function: Get-ADContact.

All done. Now, we have function that only returns AD contact objects. Additionally, we have the option of narrowing down our search by including the often used -SearchBase and -SearchScope parameters. While I don’t doubt there’s a better way, I think this one will work for now.

PSMonday #16: Monday, August 15, 2016

Topic: PowerShell Remoting Continued V

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

I think back on the last few weeks and there’s a few things we haven’t covered. Let’s say we have four servers and we want to run the same command on each one. We know we can use Invoke-Command and supply four names as the value to the -ComputerName parameter. Jump back to PSMonday #11 on July 11, 2016, for an example.

In today’s PSMonday, we’ll see how to do this another way. We’ll start by creating four PS Remoting sessions. Each session object will hold the session information for an opened session with a remote computer. Displaying the value stored in the variable $Session includes the ConfigurationName property. In the below example, you can see it’s the default endpoint — Microsoft.PowerShell — we discussed last week.

$Session = New-PSSession -Computer DC01,DC02,DC03,DC04
$Session

Id Name           ComputerName    State         ConfigurationName     Availability
-- ----           ------------    -----         -----------------     ------------
4 Session4        DC04            Opened        Microsoft.PowerShell     Available
3 Session3        DC03            Opened        Microsoft.PowerShell     Available
2 Session2        DC02            Opened        Microsoft.PowerShell     Available
1 Session1        DC01            Opened        Microsoft.PowerShell     Available

With these sessions stored in the variable $Session, we can use Invoke-Command and the -Session parameter to run commands against each computer. Here’s a slightly modify version from that previous PSMonday.

Invoke-Command -Session $Session -ScriptBlock {"**** $env:COMPUTERNAME ****"}

**** DC02 ****
**** DC04 ****
**** DC01 ****
**** DC03 ****

One benefit of using sessions is speed while running the commands against the remote computer. It’ll be marginable, until you have a good number of computers as a part of your session variable. The reason there’s a boost in speed is because you already have currently existing sessions to use. Invoke-Command doesn’t have to create sessions, and then destroy them, when it’s done. I should note that it still takes time to create the sessions prior to using them.

Another benefit is when you need to run multiple commands against the same computers, but don’t want to do it at the same time. You can connect and collect some data and then connect again later without the need to create a new connection. Think about it, you can create a variable in the session and it will continue to exist. This means your second Invoke-Command can use the variable you created with the first Invoke-Command, command. See the below example.

$Session = New-PSSession -ComputerName DC05
Invoke-Command -Session $Session -ScriptBlock {$Var = $env:COMPUTERNAME}
Start-Sleep -Seconds 5
Invoke-Command -Session $Session -ScriptBlock {$Var}

DC05

I think we may be done with PowerShell Remoting. We’ll see.

PSMonday #15: Monday, August 8, 2016

Topic: PowerShell Remoting Continued IV

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

When we use PowerShell Remoting, as we have for the last few weeks, we connect to an endpoint, or a session configuration, on a remote computer. It doesn’t matter whether we use Enter-PSSession or Invoke-Command, an endpoint is still required. So it’s been said, the term endpoint and session configuration can be used interchangeably. The Get-PSSessionConfiguration cmdlet allows you to view the endpoints on a computer, as is displayed in the below example.

Get-PSSessionConfiguration

Name          : microsoft.powershell
PSVersion     : 4.0
StartupScript :
RunAsUser     :
Permission    : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed

Name          : microsoft.powershell.workflow
PSVersion     : 4.0
StartupScript :
RunAsUser     :
Permission    : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed

Name          : microsoft.powershell32
PSVersion     : 4.0
StartupScript :
RunAsUser     :
Permission    : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed

Name          : microsoft.windows.servermanagerworkflows
PSVersion     : 3.0
StartupScript :
RunAsUser     :
Permission    : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed

The default endpoint’s name is Microsoft.PowerShell. This is to say, that if you don’t indicate which endpoint to use when you remotely connect to another computer — and we haven’t in any of these PowerShell Remoting PSMondays — then you’re connecting to this endpoint.

Notice the Permission property on the Microsoft.PowerShell endpoint. This indicates, that by default, a user must have a membership in the local administrators group, or in the Remote Management Users group, to use this endpoint. Simply put, you must have specific permissions to use a PS Remoting endpoint. It’s not something that just anyone can use on your servers by default.

The idea behind constrained endpoints, or JEA (Just Enough Administration) endpoints, as they’re now called, is that you can create your own endpoint, give regular users access to it, have it run as an elevated user (using the RunAsUser property), and limit the cmdlets and functions available in the endpoint. That’s right, we can allow non-administrators the ability to perform tasks as an administrator, without the need to ever elevate the account used to connect to the JEA endpoint. While we won’t go into this any further in a PSMonday — at least I can’t imagine we will — this information may be beneficial to know.

See you next week.

Format PowerShell Results for Outside of PowerShell

There are times I use PowerShell to help format information I need to send along to others in an email. What I mean is that I return results from PowerShell commands, and format it using PowerShell, so that I can simply send it to the clipboard and paste it into an email.

The below example gathers all the computer-related details about my Lync servers each time I open a new Windows PowerShell session. The time to complete is minimal, so I’m perfectly okay with returning all the properties on each of the servers. The extra milliseconds are worth having the most current information on these servers inside my $Lync variable. By the way, you don’t need to know anything about Lync (or Skype) to make use of the post; it’s not the point.

PS > $Lync = Get-ADComputer -Filter * -SearchBase "OU=Lync,DC=MyDomain,DC=com" -Properties *

With the variable set and assigned, I can do things like the next two, combined examples. This comes in handy all the time, and without the need to think about rewriting the command whenever it’s needed again.

PS > $Lync.Name
L-FE01
L-PC02
L-PC01
L-FE02
L-Ed01
L-FE04
L-FE03
L-Ed02

PS > $Lync | Select-Object Name,Description

Name              Description
----              ----------- 
L-FE01            Lync 2013 Front End
L-PC02            Lync 2013 Persistent Chat
L-PC01            Lync 2013 Persistent Chat
L-FE02            Lync 2013 Front End
L-Ed01            Lync 2013 Edge
L-FE04            Lync 2013 Front End
L-FE03            Lync 2013 Front End
L-Ed02            Lync 2013 Edge

Let’s consider that I need to enter the names of the servers into an email and I want them to be comma separated. Easy, we’ll the use the -join operator to complete this task

PS > $Lync.Name -join ','
L-FE01,L-PC02,L-PC01,L-FE02,L-Ed01,L-FE04,L-FE03,L-Ed02
PS >
PS > # Humm... let's add spaces, too.
PS >
PS > $Lync.Name -join ', ' # <-- Notice the trailing space.
L-FE01, L-PC02, L-PC01, L-FE02, L-Ed01, L-FE04, L-FE03, L-Ed02

Because I’m sold on PowerShell, I’ll always take extra time to use it to its full potential. What I wanted to do was add the word “and” after the last comma and a space, and before the name of the final Lync server. This will make the most sense when my text is dropped into an email and used as, or part of, a sentence. We’ll start this example by determining the location of the last comma by using the .LastIndexOf() method. This returns the location within the string.

PS > $index = (($Lync.Name) -join ', ').LastIndexOf(',')
PS > $index
54

Now that we know the location of the last comma, we can remove it and then insert what we want. The next example uses two methods. First the .Remove() method removes the comma, and then the .Insert() method adds everything the way we want it.

PS > (($Lync.Name) -join ', ').Remove($index,1).Insert($index,', and')
L-FE01, L-PC02, L-PC01, L-FE02, L-Ed01, L-FE04, L-FE03, and L-Ed02
PS >
PS > (($Lync.Name) -join ', ').Remove($index,1).Insert($index,', and') | clip.exe

In the last above line, we reran the command and piped it to clip, so that it’s ready to be pasted into my email. After you do this awhile, you find little ways in PowerShell to handle the exact formatting you want. It’s these little tasks, that will give you an opportunity to continue to practice your PowerShell. And finally, here’s the email where I entered the information I had collected and formatted in PowerShell.

format-powershell-results-for-outside-of-powershell-01

PSMonday #14: Monday, August 1, 2016

Topic: PowerShell Remoting Continued III

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Last week, we discussed how to get a locally created variable into a PS Remoting session, by taking advantage of the Using scope modifier — a feature that was added in Windows PowerShell 3.0. Today, we’ll go over how we did it in earlier versions.

In PowerShell 2.0 the Using scope modifier didn’t yet exist. To get locally declared variables into a PS Remoting session took a bit more work. Below are two examples, beginning with the most preferred, first.

The first below example requires the use of the Param block inside the value provided to Invoke-Command’s -ScriptBlock parameter. It also requires the use of Invoke-Command’s -ArgumentList parameter. The $LocalVariable was set last week with the name of the local computer. This time, we’ll take two variables into the PS Remoting session, right after we define the $Date variable. I’ve added a couple blank lines inside the script block, to make things a bit easier to read.

$Date = Get-Date

Invoke-Command -ComputerName MEMSRV01 -ScriptBlock {
    Param($String1,$String2)

    "The local computer is $String1."
    "The local recorded date and time is $String2."
    "The remote computer is $env:COMPUTERNAME."

} -ArgumentList $LocalVariable,$Date

The local computer is TOMMYSPC.
The local recorded date and time is 07/30/2016 09:30:50.
The remote computer is MEMSRV01.

So we’re sure we know what happened here, $LocalVariable went to the remote session and became the value provided to $String1, and $Date was the value provided to $String2.

Our final example uses the $args variable without the need for the Param block. It still requires the -ArgumentList parameter, however. Although this method requires a little less work, using the $args variable can become confusing and so this option should be saved for last. Realistically, it should never be used, as you can always use the above example when you’re working with PowerShell 2.0.

Invoke-Command -ComputerName MEMSRV01 -ScriptBlock {

    "The local computer is $($args[0])."
    "The local recorded date and time is $($args[1])."
    "The remote computer is $env:COMPUTERNAME."

} -ArgumentList $LocalVariable,$Date

The local computer is TOMMYSPC.
The local recorded date and time is 07/30/2016 09:30:50.
The remote computer is MEMSRV01.

And that’s how we get our local variables into our PS Remoting sessions, if the Using scope modifier isn’t an option.