Working with the lastLogon attribute in PowerShell

I have been spending a lot of time working on Powershell scripts. Specifically, I have been re-writing many VB scripts and processes into more efficient powershell versions. In my last post I mentioned that I planed to share how I made a task that took 8 hours to execute run in only 28 minutes. There are two parts to this efficiency — Start-Job and Group-Object. In a future post I’ll write more about the Start-Job functionality, but the greatest benefit was from Group-Object — and thats what I’ll focus on in this post.

The task I re-wrote looked at about 50,000 user accounts and collected many details about the accounts for auditing purposes. Due to some constraints, I needed to use the lastLogon attribute instead of lastLogonTimeStamp. You can read more about the constraints here: http://www.rlmueller.net/Last%20Logon.htm, but this line pretty much sums it up: Because the lastLogon attribute is not replicated in Active Directory, a different value can be stored in the copy of Active Directory on each Domain Controller. In VB script, I was using an LDAP bind to each domain controller for each user account and then evaluated the lastLogon attribute, which was very inefficient.

Here is the powershell version of this code, which is much more efficient and flexible (as you can get the last login time from each/all domain controllers very easy).

[cc lang=”powershell”]
$lastLogons = @()
$domainControllers | %{ # I used an LDAP query for computers with primaryGroupID=516 to get a list of domain controllers distinguished names
$objDC = New-Object DirectoryServices.DirectoryEntry “LDAP://$_”
$dcName = $objDC.dnsHostName.ToString()
$de = New-Object System.DirectoryServices.DirectoryEntry (“LDAP://$dcName”)
$Rech = New-Object System.DirectoryServices.DirectorySearcher($de)
$Rech.filter = “(&(objectCategory=User)(!objectClass=Computer)(lastLogon>=1))”
$Rech.SearchScope = “subtree”
$Rech.sizelimit = “90000”
$Rech.pagesize = “90000”
$Rech.PropertiesToLoad.Add(“distinguishedName”);
$Rech.PropertiesToLoad.Add(“lastLogon”);
$liste = $Rech.FindAll()
$lastLogons += ($liste | select @{n=’DN’;e={$_.properties.distinguishedname}},@{n=’LastLogon’;e={$_.properties.lastlogon}}, @{n=’DC’;e={$dcName}})
}
$groupedLastLogin = $lastLogons | Group-Object -Property DN -AsHashTable -AsString

# To get an individuals last login information, just select it from the hash table
$trueLastLogin = $groupedLastLogin.Item($objUser.distinguishedName.ToString()) | sort LastLogon -Descending | select -First 1
$lastDC = $trueLastLogin.DC
$lastLogonTrueDate = [datetime]::FromFileTimeUTC($trueLastLogin.lastLogon)
[/cc]

After creating this code, I attempted to select user information from the $lastLogons variable. This was very slow — even slower than the previous VB script that did excessive LDAP binds. The efficiency in this script was gained by grouping the objects into a hash table ($groupedLastLogin) by distinguished name, and then accessing the specific key value as needed.

I know several of my posts have strayed from the normal VMware/vSphere/PowerCLI topics recently, but I have some plans to get back on track early next year. Please stay tuned!

Posted in Messaging, Scripting | 2 Comments

T-SQL to show how long a task took to execute

I have been working on various scripts that populate information into a SQL table. The other day I was attempting to see how long some of these tables took to populate. I could have updated the PowerShell script to include some Measure-Command information, but then I’d have to re-execute my tasks and see how long they took to run. Since I already had all of the information populated into a table, and each row had a time stamp of when the data was entered, I could just use a T-SQL select statement to get the run time data I wanted.

SELECT CAST( (MAX(dataCollected) - MIN(dataCollected)) as time) as TotalRunTime, COUNT(*) as RecCount FROM tblTestInserts WHERE collectionID = MAX(collectionID)

The above example looks at two columns in my table. The first column (dataCollected) is of the datetime type and has a default binding of GetDate() — so it is automatically populated with the SQL Servers date and time every time a record is inserted. The second column (collectionID) is an integer that is calculated each time I execute the script — inside of powershell I select the maximum value of this column and then add 1. This provides a way know how many times the task has been executed and which data is the most current.

This gave me exactly the information I was looking for without needing to modify my PowerShell script and re-run the collection. It also allows me to compare current runs to previous runs (by changing the value of the collectionID in the WHERE clause). I can also be more specific…I can add an ‘AND’ to the where clause and see how long a specific part of the collection took to run.

In a future post I plan to share how I was able to take a vbscript that took 8 hours to complete condense it into a 28 minute execution time powershell script. (Here is a preview of the magic sauce — Group-Object -Property DN -AsHashTable -AsString)

Posted in Scripting | Leave a comment

Get-ESXCli and changes with vSphere 5.0

I mentioned in a previous post (VMware View inside vCloud Director) that we had to make a last minute change to the lab content on the fly. This change was to Exercise 11 that covers the Get-ESXCli cmdlet. I should mention that this is a change to ESXi — if you are using PowerCLI 5.0 to access a 4.1 host, you will need to use the 4.1 methods.

The PowerCLI 4.x lab can be found here: Demo Days 4: PowerCLI Lab
The PowerCLI 5.x lab can be found here: GCVMUG PowerCLI 5.0 Lab

After connecting to the ESXi hosts directly with connect-viserver, we get the ESXCli and assign it to a variable:

$esxcli = Get-ESXCli

The changes apply to how you access virtual machine information.

# ESXi 4.1:
$esxcli.vms.vm.list()

# ESXi 5.0:
$esxcli.vm.process.list()

As you can see the root element for the VM namespace has changed as well as the child namespace. This has been updated in the lab and should be corrected for the next show.

Posted in Scripting, Virtualization | Leave a comment

GCVMUG PowerCLI 5.0 Lab

Earlier this month I had the privilege of helping out with a PowerCLI hands on lab at the Greater Cincinnati VMUG regional event. The content of this GCVMUG PowerCLI 5.0 lab is now available online: GCVMUG PowerCLI 5.0 Lab

A special thanks goes to the GCVMUG, Jake Robinson, Ryan Birk, Bluelock and 10zig for making this lab possible.

Posted in Scripting, Virtualization | 1 Comment

Tools Upgrade Policy

I was looking for a way to enable “Check and upgrade Tools during power cycling” for more than one VM at a time, but without enabling for all VM’s in the cluster.
In this case our naming convention allowed for the quick and simple use of wildcards to make selections.

From our test lab, I wanted to target only VM’s with “cel” in the name.

First, verify the targeted VM’s are all set to manual.

#check policy status
Get-VM *cel* | sort name | Select Name,@{N="UpgradePolicy";E={$_.Extensiondata.Config.Tools.toolsUpgradePolicy}}

Name                                    UpgradePolicy
----                                    -------------
NAVCEL01                             	manual
WEBCEL01                             	manual
WEBCEL02                             	manual
DEVCEL01                             	manual
DEVCEL02                             	manual
DEVCEL03                             	manual

Next, update the policy (effectively checking the box)

[cc lang=”powershell”]
#change the UpgradePolicy to upgradeAtPowerCycle
$vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
$vmConfigSpec.Tools = New-Object VMware.Vim.ToolsConfigInfo
$vmConfigSpec.Tools.ToolsUpgradePolicy = “UpgradeAtPowerCycle”

Get-VM *cel* | %{
$_.Extensiondata.ReconfigVM($vmConfigSpec)
}
[/cc]

Now to verify the targeted VM’s are all set to upgradeAtPowerCycle

#check policy status
Get-VM *cel* | sort name | Select Name,@{N="UpgradePolicy";E={$_.Extensiondata.Config.Tools.toolsUpgradePolicy}}


Name                                    UpgradePolicy
----                                    -------------
NAVCEL01                             	upgradeAtPowerCycle
WEBCEL01                             	upgradeAtPowerCycle
WEBCEL02                             	upgradeAtPowerCycle
DEVCEL01                             	upgradeAtPowerCycle
DEVCEL02                             	upgradeAtPowerCycle
DEVCEL03                             	upgradeAtPowerCycle

In the link below, you’ll find code to update all VM’s in a cluster.

Sources/Credits: Community member LucD at communities.vmware.com/message/1601811

Posted in Scripting, Virtualization | Leave a comment