Monthly Archives: September 2015

Compare Two Arrays for Differences

I realized how obsessed I am with much I appreciate Windows PowerShell today, as I was working with Exchange 2013. I needed to find the differences in access rights between a PublishingEditor and Editor. The image below, pulled from a Microsoft web page, lists the different access rights.

Compare Two Arrays for the Differences01

One way to determine the difference would be to sit and stare at the screen. Nah. It didn’t take my eyes long, darting back and forth between lines, to desire a better way. Another way would be to take a screen capture and mark off the common entries. The image below shows what that looks like. Seriously, though, I can do better. Let’s do this in PowerShell and give ourselves a little practice. More on that in a moment.

Compare Two Arrays for the Differences02

I copied and pasted the first line I wanted to my PowerShell console.

PS> PublishingEditor   CreateItems, ReadItems, CreateSubfolders, FolderVisible, EditOwnedItems, EditAllItems, DeleteOwnedItems, DeleteAllItems

Then, I bounced around the entry to ensure that $PublishingEditor became a variable, by adding a dollar sign. I also added the =  assignment operator and put single quotes around the entire group of access rights, but this wasn’t quite enough.

PS> $PublishingEditor = 'CreateItems, ReadItems, CreateSubfolders, FolderVisible, EditOwnedItems, EditAllItems, DeleteOwnedItems, DeleteAllItems'

The next thing I did was add a couple methods — one right after the other, at the end of the command. The first method I added was .Replace(). In the version I added, .Replace(‘ ‘,”), it replaces all single spaces with nothing, effectively removing the spaces. The next method I added was .Split(). This, when added as .Split(‘,’), splits the string at each comma.

PS> $PublishingEditor = 'CreateItems, ReadItems, CreateSubfolders, FolderVisible, EditOwnedItems, EditAllItems, DeleteOwnedItems, DeleteAllItems'.Replace(' ','').Split(',')

Next, I copied the second line, for Editor access rights, pasted it in the console and cleaned it up, too.

PS> $Editor = 'CreateItems, ReadItems, FolderVisible, EditOwnedItems, EditAllItems, DeleteOwnedItems, DeleteAllItems'.Replace(' ','').Split(',')

At this point, I had two variables, $PublishingEditor and $Editor. I needed a way to compare the two variables to determine which one had additional access rights (presumably PublishingEditor) and what those were.

The first thing I did was echo the variables’ values to my screen. The second, was to run a comparison of the two. This was done using the Compare-Object cmdlet, where I provided one variable, $PublishingEditor, as the value for the -ReferenceObject parameter, and the other variable, $Editor, as the value for the -DifferenceObject parameter.

PS> $PublishingEditor
CreateItems
ReadItems
CreateSubfolders
FolderVisible
EditOwnedItems
EditAllItems
DeleteOwnedItems
DeleteAllItems
PS>
PS> $Editor
CreateItems
ReadItems
FolderVisible
EditOwnedItems
EditAllItems
DeleteOwnedItems
DeleteAllItems
PS>
PS> Compare-Object -ReferenceObject $PublishingEditor -DifferenceObject $Editor

InputObject                                                 SideIndicator
-----------                                                 -------------
CreateSubfolders                                            <=

The results, above, indicate that $PublishingEditor (the InputObject) has an additional access right, called CreateSubfolders. As you can see, the default results of Compare-Object only show the differences. Had I used the -IncludeEqual parameter the results would have looked like the example below.

PS> Compare-Object -ReferenceObject $PublishingEditor -DifferenceObject $Editor -IncludeEqual

InputObject                                                 SideIndicator
-----------                                                 -------------
CreateItems                                                 ==
ReadItems                                                   ==
FolderVisible                                               ==
EditOwnedItems                                              ==
EditAllItems                                                ==
DeleteOwnedItems                                            ==
DeleteAllItems                                              ==
CreateSubfolders                                            <=

Thanks for reading. Now off to find some other way to use PowerShell to speed up my day.

Before I do that, though, I mentioned practice earlier in this post. To really get PowerShell, we need to take every opportunity to continue to learn, but also to practice what we already know. This was a perfect opportunity. Not only would I get the results I needed — the difference in access rights — but I also got a half of minute of working with the Compare-Object cmdlet and the .Replace() and .Split() methods, where I may not have otherwise.

The more I use PowerShell, the less I have to try and remember when I really need it.

Getting Local Variables into Remote Sessions

There are at least three ways to get your local variables into your remote sessions. I’ve written these in preferential order beginning with the best option. Don’t forget to read about_Remote_Variables (Get-Help -Name about_Remote_Variables) to learn more, and see more examples.

$Word1 = 'PowerShell'
$Word2 = 'on'
$Computer = 'Server1'

# Using Scope Modifier
# (1st Option -- works in PowerShell 3.0 and up)
Invoke-Command -ComputerName $Computer -ScriptBlock {
    Write-Verbose -Message "$Using:Word1 $Using:Word2 $env:COMPUTERNAME (Using Scope Modifer)" -Verbose
}
VERBOSE: PowerShell on Server1 (Using Scope Modifier)


# Param
# (2nd Option -- works in PowerShell 2.0 and up)
Invoke-Command -ComputerName $Computer -ScriptBlock {
    Param($Word1InParam,$Word2InParam)
    Write-Verbose -Message "$Word1InParam $Word2InParam $env:COMPUTERNAME (Param)" -Verbose
} -ArgumentList $Word1, $Word2
VERBOSE: PowerShell on Server1 (Param)


# Args
# (3rd Option -- works in PowerShell 2.0 and up)
Invoke-Command -ComputerName $Computer -ScriptBlock {
    Write-Verbose -Message "$($args[0]) $($args[1]) $env:COMPUTERNAME (Args)" -Verbose
} -ArgumentList $Word1, $Word2
VERBOSE: PowerShell on Server1 (Args)

Report on Active Directory Objects in Abandoned Organizational Unit

Before we really start this post, I should mentioned that there’s no reason that the script discussed in this post can’t be run against an Organizational Unit (OU) that hasn’t been abandoned. It just worked out that I wrote the script in order to determine if an OU had be abandoned.

I threw a small script together in the last couple days and thought I’d share it. The reason for the script was because I may have had an Active Directory (AD) OU that was no longer being used. In order to determine if this was really the case, I wanted to check various properties on the user and computer objects in the OU, to include any nested OUs. These properties included the last logon time stamp, the last date the objects changed, and a few others.

The first couple of lines in the script set two different variables. The first one stores the Domain’s Distinguished Name, and the second one is assigned the location of the abandoned OU. The second variable is based partly on the first. This script requires the ActiveDirectory module and assumes it’s being run on PowerShell 3.0 or greater, as the AD module isn’t explicitly imported.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

In the next part of the script, we start to send the $AbandonedOU variable’s objects across the pipeline, to the Foreach-Object cmdlet. As each object passes across, we determine what type of AD object we’re dealing with. If it’s a user object, we set the $Command variable to the string, Get-ADUser. If it’s a computer object we set the $Command variable to the string, Get-ADComputer. If it’s neither, such as a nested OU, we’ll return to the $AbandonedOU variable and send the next object without assigning anything to the $Command variable (or running any of the upcoming code).

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

Providing we have a user or computer AD object, we’ll run the code in the next example. This will execute the cmdlet, whether it be Get-ADUser or Get-ADComputer, returning the requested properties that we then calculate (think, customize).

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
}

Finally, we sort the collection of objects we’ve returned and customized, and in my case, pump the data out to a CSV file at the root of my C:\ drive. As you’ll see below, I’ve included both the code in the previous example and the additional code.

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

I want to mention something about the line above that calculates the “Added To Domain By” property. In many environments this is going to only be <DOMAIN>\Domain Admins. The reason I added this, is because in the AD environment in which this ran, users, other than the Domain Admins, can join computers. I know this is a default; however, in many environments it is not allowed. This may or may not be a helpful property in your environment.

Cheers, and thanks for reading! I’ve included the complete script below.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

Twitter Reply – A Desired State Configuration Print Book

First things first: This post has been written for quite some time. It’s that I’ve been somewhat apprehensive about publishing it. Well, I’m tired of waiting, I’ve read it too many times now, and I just need to get it over with already. These are my opinions, and I’m smart enough, and big enough, to admit when my opinions are wrong.

It’s one thing to leave an opinion on Twitter, and another to have to explain why you “said” what you did. On Tuesday, August 25th, I tweeted about wanting a print DSC (Desired State Configuration) book, and mentioned Don Jones, Steve Murawski, and Jeffery Hicks — a well-qualified team to make something like that happen.

Both Don and Steve expressed the same sentiment: “DSC is likely to change faster than the print cycle can keep up,” and “agree with Steve. Print can’t keep up.” Okay, I could have left it there (because I trust those guys are probably right), and probably would have, until Don asked a follow up question: “curious why print is important for you.” Oh… well, now I needed an organized approach as to why I wanted a print DSC book, more than just to say, “Uh…, because.”

Besides “because,” I’ve organized my reply into a few different points. I certainly don’t think this will change the minds of people like Don and Steve, or get the Manning folks running around the office doing high tens, while chanting my name (it’s Tommy Maynard, just in case). An awesome visual, but not likely.

The de facto standard in Windows PowerShell publishing was written by Don Jones and Jeffery Hicks, and printed by Manning Publications. Learn Windows PowerShell in a Month of Lunches has become the book to use, to learn PowerShell. Mentions of it are everywhere. It comes up on Reddit every couple weeks, I see it on Microsoft TechNet, PowerShell.org (of course), and (also, of course) hear it mentioned on the PowerScripting Podcast. People that use those forums are always asking: how do I learn?, where do I learn?, and more often than not, this print book title comes up. It comes up even when people aren’t even asking for a PowerShell resource. I’ve said it myself: “You’d being doing yourself a favor by reading this book…,” after seeing that they’ve asked a question the book directly answered: “Why can’t I use Get-Process right after Get-Service?” — I just read this one again, somewhere.

Every religion needs a bible, and so DSC needs start to finish guide — a single, bound resource using PowerShell 4.0 and 5.0, that highlights those differences, as it walks people though DSC, that already have an understanding of PowerShell. If “DSC is the ultimate outcome of PowerShell,” (http://powershell.org/wp/2014/03/03/dsc-must-have-or-just-nice-to-have) then how can we skip the progression that is Learn Windows PowerShell in a Month of Lunches and Learn PowerShell Toolmaking in a Month of Lunches?

After reading the Learn Windows PowerShell in a Month of Lunches and Toolmaking book, I came to realize that I could have easily decreased the time it took to learn PowerShell, had I not done it on my own. These books explained things I ended up learning all over the place. I learned with help files and about topics, by reading questions and answers in various forums, and by finding random blogs on Twitter to consume. This bounce around, sporadic method of learning left holes, and it wasn’t something I realized until after I read those two titles. See, my specific reason to read those two books was to help fill in any cracks I may have missed — and there were plenty. To not have a trustworthy source to do the same thing with DSC concerns me.

I’m aware that Microsoft has online resources, but equally so, that books are written in a way that educates the user, not written for an existing expert that needs a reminder or refresher, or to clarify something that they almost already know. I recently read Jason Helmick‘s IIS book and I am a few chapters into Richard Siddaway‘s Active Directory book. I expect the same thing to happen: I’m going to read things, that I already know — lots of it. But still, in the end, I’ll feel confident I gave myself a thorough run down of the topic. It’s impossible to know that I’m not missing something when bouncing from blog to whitepaper to forum.

I know nothing about the time and involvement in getting a published book to market, but I can appreciate that it’s a long and involved process. You don’t have to read too many acknowledgement pages to understand that it takes a good number of people, a good amount of work, over a good amount of time. The timing just seems right here. We’re getting into PowerShell 5.0 heavily now. It comes with new cmdlets, and new declarations for DSC. There’s new DSC resources all time. I can’t think of a better time to ensure the community members — those coming from PowerShell, and even those joining fresh — have a beginning to end DSC walk though and print reference. Just as the MoL (Month of Lunches) PowerShell books are a good starting place, even when they reference PowerShell 3.0 (two versions back), DSC needs a print beginning.

In closing, I think it should be reiterated that even in 2015, where I never have to be away from cat pictures, idiotic Facebook political posts that indicate the education level of old high school “friends,” and instant news updates, we should have a print book on Desired State Configuration. Give me something to recommend, let me make sure I don’t miss anything in my continued effort to learn, and help me take advantage of the relative newness of this topic. Feed us the fundamentals of DSC — how much are those going to change? If we can end up where we are now with the MoL PowerShell books, then why don’t we start the progression with DSC? Jeffery Hicks, when asked why there aren’t updates to MoL, says, “the fundamentals haven’t really changed since PowerShell 3.0. In fact some fundamentals are unchanged since v2” http://jdhitsolutions.com/blog/powershell/4478/updating-month-of-lunches. How much of the current fundamentals of DSC are going to change in PowerShell 6.0 (DSC 3) and 7.0 (DSC 4)? It’s moving faster than print, I can appreciate that, but is it going to veer so severely that a print book won’t work?

It was a special day when I was able to enforce a registry setting via DSC, even though it’s so small and simple. Give me more; walk me though more defining moments. Help make me the expert. Prepare me for a career in DevOps. Help me ensure I have a complete understand of Desired State Configuration, and if it moves too fast, then align yourself with Microsoft. Ask them to partner in the effort to include everything up to the point of publication. Snover loves this community; his favorite conference is the PowerShell Summit (unless he says that about all the conferences he attends).

In closing (really this time, I promise), I want to mention the DSC eBook provided by PowerShell.org. It’s bound to come up. I’m certainly aware of its existence and that print copies were distributed at the last TechEd, or first Ignite — I don’t remember which one, and I wasn’t there for either. I’ve downloaded it and used it. I was a great first step, but when I first downloaded it, it seemed written out of order, only helped with minimal deployment scenarios (Windows backup), and seemed to lack a full walk though of the product. I went to grab it now, and the PDF version on Penflip wasn’t a complete version. It stopped after the “Where to find Resources” chapter. I think this was part of my frustration, although it seems isolated to the pdf version. The text and Word versions included the other chapters.

Thanks to anyone that read these opinions. Again, that’s all they are, and really, I’m not sure how influential they really are, or if I even really made much of a case for print.

Script Sharing – Functions and Code from My Profile

Over the last couple of years, my profile ($PROFILE) has filled up with a bunch of little functions that I use to help shave seconds off my day. I’ve shared a few in other posts, but here’s a few more that I’ve yet to share, that might be interesting. As a forewarning, in many of these, I understand that there may be a better way to do things.

This first one uses Google to show me the definition of a word. It isn’t written to return results to the Windows PowerShell console, but instead will launch the definition in a web browser. It was good enough at the time, and still is on a occasion (when I don’t actually know what a word means ;)) .

Set-Alias -Name lookup -Value Get-Definition
Function Get-Definition {
    Param (
        [Parameter(Mandatory = $true)]
        [string]$Word
    )
        Start-Process "https://www.google.com/?gws_rd=ssl#q=define:$Word"
}

Here’s an example I just used today, when I read Don Jones’ post about Jeffrey Snover’s promotion to Technical Fellow: http://donjones.com/2015/09/02/congratulations-jsnover-a-well-earned-honor.

Functions and Code from my Profile ($PROFILE)01

And, because why not, here’s a photo of Jeffrey Snover and me. For those of you new to PowerShell, I’m on the right; Snover, PowerShell’s inventor, is on the left. Thanks to PowerShell.org and Will for the photo.

Me and Snover (front)

This next inclusion isn’t a function, but has turned out to be quite helpful. I recently posted it on Twitter. By entering the alias “snip” into the console, I’m able to quickly open the snipping tool. In fact, I just used the alias to grab the screen capture, for the Get-Definition function, above.

Set-Alias -Name snip -Value "$env:SystemRoot\system32\SnippingTool.exe"

Here’s a screen capture of the Snipping Tool program.

Functions-and-Code-from-my-Profile-PROFILE02

This next one is also not a function, but extremely helpful, and likely to be some of the oldest code in my profile. This, while quite small, saves me all the time. Here’s how it works: When the PowerShell console opens, it checks the title bar — the WindowsTitle — for any left or right square brackets: this [, or this ]. If it doesn’t find them, it then modifies the WindowsTitle to include my computer’s name inside square brackets. It is extremely handy to be able to quickly verify that I’m typing in to the console on my machine and not inside a console on a RDP session. Yes, I still RDP for some things, and yes, I will also, at times, open a PowerShell console inside the RDP session (as strange as that may be).

# Set Console Window Title
If (-not($Host.UI.RawUI.WindowTitle -like "*`[*`]*")) {
    $Host.UI.RawUI.WindowTitle += " [$($env:COMPUTERNAME)]"
}

Since I’ll occasionally take and upload screen captures (see the first example, above), I wanted a quick way to remove my edited WindowsTitle, so it didn’t show my computer’s name. That gave way to this micro function to return the WindowsTitle to its default (yes, it’s hard coded) — my console is always elevated (however, the log on to my computer is not). I suppose what I should do, is assign the default text, when the console is first opened, and before the WindowsTitle is changed, into a variable for later use in this function.

# Set Default Console Window Title
Function Set-WindowTitleDefault {
    $Host.UI.RawUI.WindowTitle = 'Administrator: Windows PowerShell'
}

The last function I’ll share today is called Get-PSRemotingSession and it allows me to see if anyone is actively using PowerShell Remoting on a specific computer, or computers. It’s basically a wrapper function for Get-WSManInstance (without having to remember the required parameters).

Function Get-PSRemotingSession([string[]]$ComputerName) {
    Foreach ($C in $ComputerName) {
        Get-WSManInstance -ComputerName $C -ResourceURI Shell -Enumerate | Select-Object -Property Owner,ClientIP
    }
}

That’s all I’ve got for today. I hope that some of these might have been helpful enough to incorporate into your profile. If there’s things you can’t live without in your profile, that you want to share, then comment below, or share them on Twitter.

Update: The last function I introduced, Get-PSRemotingSession, has been updated. Only returning the Owner and ClientIP was fine when I only ran it against a single computer. The problem, when I ran it against multiple computers, is that I didn’t know the name of the computer that had the remote session — it wasn’t included in the output. Therefore, I updated the function, as seen below, so that it’ll indicate the computer name. It can’t get the computer name as part of what’s returned from Get-WSManInstance (without resolving the ClientIP), but it can based on which computer it’s checking to begin with (the value in $C). Here’s the updated version:

Function Get-PSRemotingSession([string[]]$ComputerName) {
    Foreach ($C in $ComputerName) {
        Get-WSManInstance -ComputerName $C -ResourceURI Shell -Enumerate | Select-Object -Property @{N='ComputerName';E={$C}},Owner,ClientIP
    }
}