Graeme Bray

Real World Automation and Deployment

Why you should revisit your code

leave a comment »

I recently had a script sent to me. This script was originally written at the end of 2011, edited in early 2012. Per the comment block at the top the script, it was version 5. It was a good script. It did what was needed for the end users.

It’s now 2014. We’ve upgraded the domain to Forest and Domain Functional Levels of 2008 R2. We’ve upgraded all of the DC’s to 2008 R2 or greater. We don’t need to use the Quest AD CMDlets anymore. They’re deprecated (in my eyes).

Here is the majority of the code snippet (I’ve edited out company information).

#Checks to see if the Quest Add on is installed.  If not installed, Installs it.
$snap_ins = Get-PSSnapin -Name Quest.ActiveRoles.ADManagement

if (!$snap_ins)
{
	Add-PSSnapin Quest.ActiveRoles.ADManagement       
}
#Loads the variable with the data from the Active Directory
$serviceaccounts = Get-QADUser -SizeLimit 0 -SearchRoot 'mydomain.local/MyOU/Accounts/Users'

#Recursively loops through every entry in $serviceaccounts variable
foreach ($account in $serviceaccounts)
{
    $contains = 0
    
    #Stores all the information about the memberships of the service account into the $memberdetails variable
    $memberdetails = Get-QADmemberof $account

    #Recursively loops through each membership per service account
    foreach ($group in $memberdetails)
    {
        #If there exists a membership that starts with "SEC", sets the flag to 1
    	if (($group.Name.StartsWith("SEC")))
    	{	            
    		$Contains = 1                	
    	}      
    }
        #If User has/hasn't a Membership starting with "SEC" then output the correct message
        if ($contains -eq 1)
        {
            write-host $account.name "has the group"
            $yes = $account.name | Out-File -Append $containspath
            $contains = 0
        }
        else
        {
            Write-Host $account.name "Does not have the group" 
            $no = $account.name | Out-File -Append $nocontainspath
        }
    #Resets the variable
    $contains = 0       
}

This was good, in 2011. We didn’t have the ability to run the AD cmdlets. We didn’t have Powershell 3.0, or 4.0, or even 5.0 (Preview – Don’t run in production, duh).

There are approximately 2000 users in this OU location that we were needing to check to see if they had any of a specific group type.

I was asked a question (at this point, I don’t remember what), but I was told that the script “works.” I looked at it and said, I’m going to re-write this. For fun, I took what was said at TechEd, to heart. I wanted to remove the Write-Host, I wanted to improve the run speed, and I wanted to make this script so it could be customized a little easier.

First, I ran Measure-Command on the entire script. Here are the results:
Old Script - Run Time
Yikes. Almost 15 minutes. That’s running sequentially, writing output to the screen. Ick.

I decided, lets re-write the script. I didn’t do it *all* on my own, but I did realize that you cannot do a -like, or -match, or -anything with a wildcard for the MemberOf property of a Get-ADUser.

<#
.SYNOPSIS
   This script will output two sets of data - one providing users in a group, the other providing users *not* in a group.
.DESCRIPTION
   This script provides the ability to take 3 parameters (Domain, GroupName) and will search in
   the specified Domain for that group.  If a SearchBase is specified, it will use that as a modifier and significantly
   speed up the script as you are searching a specific OU.

   This script will output to two different files, each appended with a specific date.

   This script can be modified to suit your specific needs.
.EXAMPLE
   Get-GroupInformation
.EXAMPLE
   Get-GroupInformation -Domain mydomain.local -GroupName SEC-*
.OUTPUTS
   Script will output 2 files to:
    "C:\Admin\Scripts\Audit_$GroupName_Security_Group\
#>
function Get-GroupInformation
{
    [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 
                  SupportsShouldProcess=$true, 
                  PositionalBinding=$false,
                  HelpUri = 'http://www.microsoft.com/',
                  ConfirmImpact='Medium')]
    [OutputType([String])]
    Param
    (
        # Domain - ex: mydomain.local
        [Parameter(ParameterSetName='Domain name')]
        [string]
        $Domain = 'mydomain.local',

        # GroupName - ex: GRP-*
        [Parameter(ParameterSetName='Group name')]
        [string]
        $GroupName = 'SEC-*'
    )

    Begin
    {
        Write-Verbose 'Defining/creating variables'
        $SearchBase = 'OU=Users,OU=Accounts,OU=MyOu,DC=MyDomain,DC=Local'
        $varUserArray = @()

    }
    Process
    {
        Write-Verbose 'Getting list of Users from Active Directory'
        try
        {
            $ADQuery = Get-ADUser -Server $Domain -Properties memberof -Filter * -SearchBase $SearchBase
        }
        catch [System.Net.WebException],[System.Exception]
        {
            Write-Error 'AD Query returned no users.'
        }
        
        Write-Verbose 'Looking through users, creating array with UserName and Groups'
        foreach ($User in $ADQuery) {
            $Groups = $User.memberof -join ';'
            $varUserArray += New-Object psObject -Property @{'User'=$User.name;'Groups'= $Groups}
        } #End ForEach User
    }
    End
    {
        Write-Verbose 'Sorting users and outputting to appropriate Output File'
        $Member = $varUserArray | Where-Object {($_.groups -match $GroupName) } | Select-Object User | Out-File D:\Workspace\Graeme\Output\Member.txt -Force
        $NonMember = $varUserArray| Where-Object {!($_.groups -match $GroupName) } | Select-Object User | Out-File D:\Workspace\Graeme\Output\NonMember.txt -Force
    }
}

Get-GroupInformation

This provided the exact same results. Bingo. This script took….
New Script - Run Time

4 Seconds!

Wow. Plus, with the new script you get some of the standard cmdlet options (Verbose for one), and it’s more easily edited.

*Note* – It is very possible that the script may not work correctly. I haven’t fully tested, but I was so blown away by the time results, that it had to be said…and posted.

Advertisements

Written by Graeme

May 30, 2014 at 4:59 AM

Posted in PowerShell

Tagged with

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: