Monday, April 20, 2015

Employing PowerShell to help you clean up the Enterprise

Evening!

Lately I've gotten heavy into PowerShell scripting, and relying on PowerShell to do my dirty work for me. When you get into the thousands of anything, you're going to need help from some sort of scripting or automation efforts. Since Citrix, VMware, and Microsoft give us some pretty sweet PowerShell commandlets for their products, I like relying on PowerShell for help.

In this particular article, I'm also going to throw in some help from Quest with their ActiveRoles Management Shell for Active Directory, because I have access to that nifty tool in some of my supported environments. It's a great tool, and comes in very handy when either the native Microsoft PowerShell modules won't do, or in some cases, AD Web Services are not running on your domain controllers.

NOTE: I recommend this to EVERYONE - before you go blindly executing PowerShell commands, KNOW what the outcome will be ahead of time. The commands in this article have the potential to be VERY destructive to your enterprise. Proceed with EXTREME caution, and I take NO responsibility for the outcome of the commands you execute. This is designed to help you automate some basic tasks.

Prerequisites:
-RSAT Tools for Windows 7
-Quest ActiveRoles Management Shell for Active Directory

1. Sorting through XenApp or XenDesktop user profiles to determine deleted or disabled users that no longer require a profile on your file server.

Assuming your naming convention on your XenApp and XenDesktop user profiles are out-of-the-box, and are just named after the user, then this one should be really simple for you.

So, in this example, you have a user profile share on a profile server...

Profile Server Name: OWENS-PF01
Share Name / Folder Name: UserProfiles

From the machine where you'll be doing the work, open up a PowerShell command prompt, and in the below example, I'll be using the drive letter N: as the mapped drive.

Step 1 - Map the drive
PS C:\Users\Owens>net use n: \\owens-pf01\userprofiles

Step 2 - Once the drive is mapped, change directory to the mapped drive
PS C:\Users\Owens>N:

Step 3 - Use the PS commandlet "Get-ChildItem" to generate a list of user profiles, and export them to a text file on your desktop. I'll use Format-Table to remove the table header, and just retrieve raw data.
PS N:\>Get-ChildItem | select Name | ft -HideTableHeaders > C:\Users\Owens\Desktop\profiles.txt

OK. You just mapped a drive to your profile share, issued a "Get-ChildItem" commandlet, which outputs all of the child objects in the profile share (the user profiles), and you selected only the NAME parameter, which is the names of the individual folders. You're removing the table headers where it tells you what the parameter type is, and then exporting the data to a text file on your desktop.

Now you have a long text file with individual usernames in it. I'd open up the text file to do some quick cleanup, removing any blank lines or things that have little or no value. Sometimes people will manage to create a random folder that has no value but just files in it, etc.

Once the text file looks good, it's time to run your queries. This is where (for me at least) the Quest ActiveRoles Management Shell comes into play. Quest Tools are available from Dell in their enterprise products, and I like it. I think it's worth the time and money.

Step 1 - Establish an array of the individual usernames in the domain (the folder names) - note below that I'm using "gc" - this is shorthand for Get-Content in PowerShell. This will import the full text file line-by-line and place the individual items in an array variable.
PS C:\Users\Owens>$aryUsers = @(gc C:\Users\Owens\Desktop\profiles.txt)

Step 2 - I like to do this to verify that the array established successfully by checking the length of the variable. This command will return a value that's basically the number of lines in the text file if you did step 1 correctly.
PS C:\Users\Owens>$aryUsers.Length

Step 3 - Run the usernames in the array against an AD query to determine their status in AD. If they have been deleted altogether, just delete their user profiles (assuming there's no file retention policies required). For the disabled users, make a judgement call as to whether or not their profile gets to stay.

For this, I like to create a separate text file that has the list of disabled or deleted users so I can just open it and go through the list when deleting. This can also be scripted, but this one requires a little more work. For the below example, I'll make a text file ahead of time on my desktop called "disabledusers.txt"

Disabled Users
1. PS C:\Users\Owens>foreach ($user in $aryUsers)
2. >>{
3. >>$userObject = (Get-QADUser $user)
4. >>Start-Sleep -Seconds 1
5. >>if ($userObject.AccountIsDisabled -eq "True")
6. >>{
7. >>Add-Content C:\Users\Owens\Desktop\disabledusers.txt "$user"
8. >>}
9. >>}
10. >>

By executing this, PowerShell is running a FOR EACH loop through each item in the array.

In line 3, we establish a new variable, which is an user object. This contains all information that Get-QADUser can provide into a single object. We're only looking for one parameter, which is the "AccountIsDisabled" parameter - it's a true / false value that says whether or not the user account is disabled.

Line 4 is important in a big enterprise. Quest's AD tools are a little resource intensive for a domain controller, so by repeatedly running the commands without pausing, you'll notice a spike in CPU or memory usage on your domain controller(s). I always insert a 1 second delay between each query to moderate the CPU usage by utilizing the Start-Sleep command.

In line 5, we test the case where AccountIsDisabled is equal to "True". If the field is true, then the user account is disabled. Inside the test case, we add the user's name to the list of usernames in the "disabledusers.txt" text file. For each disabled user, you'll get a new line in your text file with their username.

After it's finished, you'll have a full list of disabled users that reside on your profile share.

The new text file in this example below will be "deletedusers.txt" on my desktop.

Deleted Users
1. PS C:\Users\Owens>foreach ($user in $aryUsers)
2. >>{
3. >>$userObject = (Get-QADUser $user)
4. >>Start-Sleep -Seconds 1
5. >>if ($userObject -eq $null)
6. >>{
7. >>Add-Content C:\Users\Owens\Desktop\deletedusers.txt "$user"
8. >>}
9. >>}
10. >>

This is essentially the same script as before, but in like 5, we're testing for the case where the user object is equal to a NULL value. In line 3 when it established the user object, if the user doesn't exist, then the $userObject variable will end up being a null value. This is represented by "$null" in PS. If the $userObject variable is empty, it'll be equal to $null.

The deleted users list is likely the one you'll want to be a little more aggressive with. You can proceed with a delete script, which will be another foreach loop run against the deleted users list. It'll look something like this...

1. PS N:\>foreach ($user in $aryDeletedUsers)
2. >>{
3. >>Remote-Item "$user" -Force
4. >>}
5. >>

This will save you the time of deleting each individual folder.

1. Creating a list of hosted XenDesktops, their assigned users, and then determining if the users are deleted or disabled.

I'll give you the different methods for the different versions of XenDesktop, along with the prerequisites.

XenDesktop 4
Prerequisites
Desktop Delivery Controller SDK
Add-PSSnapin XdCommands*

Command that exports a list of virtual desktops and their respective users:
Get-XdVirtualDesktop | select Name,AssignedUserName

The Name value is the name of the virtual desktop.
The AssignedUserName value is the name of the user assigned to the machine, in the form of "DOMAIN\Username"

XenDesktop 5.x
Prerequisites
Citrix Desktop Studio
Add-PSSnapin Citrix*

Command that exports a list of virtual desktops and their respective users:
Get-BrokerMachine | select MachineName,AssociatedUserName


The MachineName value is the name of the virtual desktop.
The AssociatedUserName value is the name of the user assigned to the machine, in the form of "DOMAIN\Username"

XenDesktop 7.x
Prerequisites
Citrix Studio
Add-PSSnapin Citrix*

Command that exports a list of virtual desktops and their respective users:
Get-BrokerMachine | select MachineName,AssociatedUserName


The MachineName value is the name of the virtual desktop.
The AssociatedUserName value is the name of the user assigned to the machine, in the form of "DOMAIN\Username"

After you have your list, you can use something like Excel to extract the username field, and run the same exact queries that I showed you in section 1 to find out whether or not they are disabled or deleted.

Hopefully this helps you. In a large enterprise, roles tend to be separated, and in my experience, the Citrix guy doesn't onboard or offboard users to the company for IT resources. We just make the machines available to users. Sometimes resources are left behind, and we need to clean them up. Get PowerShell to do the work for you :)

Have a great night, and as always, happy Citrixing!

No comments:

Post a Comment

2016 Subaru WRX - Replacing the Starlink Head Unit

I've spent a lot of time and WAY too much money on this project, so I figured I might be able to help someone else out with their 2016+ ...