Generate Test Data for Exchange 2010 Using PowerShell: Simulate User Logons

Hopefully, you already know of the importance of maintaining a test environment. One of the challenges is configuring your test or lab setting as close to your production environment as possible. Or, at the very least having data so that when you run a command, there is a tangible or measurable result. AI practically live in a test environment, so I’ve had to come up with a variety of tools, tricks, and code to create more of a real-world environment. In this article I want to focus on Exchange — specifically, generating test data for Exchange 2010 using PowerShell.

One piece of test data I’d like to have is a user logon so that when I run a cmdlet like Get-MailboxStatistic, the LastLogonTime property has a value. Of course, I don’t want to have to manually log into the Exchange server with each test user account. There is a Test-OWAConnectivity cmdlet that I could also use. But again, I would have to manually enter credentials. I have over 100 mailbox-enabled users, so that isn’t very practical. Instead I’m going to turn to PowerShell 3.0 and take advantage of the new web cmdlets.
This article is a two-parter: In this first article, I’ll discuss how to simulate user logons. In part two, I’ll talk about sending mail messages.

Creating Test Data: Log onto Outlook Web Access

I can meet my goals by logging into Outlook Web Access (OWA) from my client desktop using the new Invoke-WebRequest cmdlet. The tricky part is handling the forms, but I’ll walk you through that.
First, I’ll define the URL for my OWA server.

[string]$Uri = "http://chi-ex01.globomantics.local/owa"

Next, I’ll invoke the web request and create a session variable called OWA.

$r = Invoke-WebRequest $uri -SessionVariable owa

The response object includes a property called Forms; as the name implies, this includes any HTML forms. There should only be one.

$form = $r.Forms[0]

Now to fill out the form. Anywhere along this process you can look at the variables in PowerShell to see what you’re working with.

$Form.fields.username= $username
$form.Fields.password= $password

The username will be in either the domain\username format or their mail address. The password is entered as plain text. Note: Because this is a secure test lab, I have no qualms about storing the password in my script file. For the sake of convenience, I’ve also set all users to use the same password.
Now I need to post the form back to Outlook Web Access, again using Invoke-WebRequest.

$r = Invoke-WebRequest -Uri "$uri/auth/owaauth.dll"-WebSession $owa -Method POST -Body $form.Fields

Normally the URI value can be found in the form object, but in this case it is only a relative path. After some experimentation I found a path that works: The request body is the hashtable of form fields, e.g. username and password.

Language and Location Settings

Here’s where it gets interesting. If your user has logged onto OWA before, there’s nothing else to do — the logon is complete. However, the first time a user logs on, they are presented with a second form to configure language and location settings. So, I have to check the response object again and see if it contains the language form.

if ($r.forms[0].id -eq "lngfrm") {
$form = $r.Forms[0]

The form fields I need to fill out aren’t apparent if you look at the $form. You need to pass the values for language and location.

$form.Fields.add("lcid",$r.ParsedHtml.getElementById("selLng").value)
$form.Fields.add("tzid",$r.ParsedHtml.getElementById("selTZ").value)

These commands will fill the form using the default values based on my current culture settings. If you want to see what other values you could use run commands like this.

#list TimeZones
$r.ParsedHtml.getElementById("selTz") | Select Text
 
#list Languages
$r.ParsedHtml.getElementById("selLng") | Select Text

Again, I need to invoke another web request, passing this form.

$r = Invoke-WebRequest -Uri "$uri/lang.owa"-WebSession $owa -Method $form.Method -Body $form.fields

At this point the logon is complete. You can always check the response object’s StatusDescription property to see if there were any errors. But now I have data for the last logon as well as when I run Get-LogonStatistics.
I put all of this in a function I call Set-OWALogon. You can see my code below, but you can also click on the link to download the PowerShell code.

Function Set-OWALogon {
 
[cmdletbinding()]
 
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipelineByPropertyName=$True,
ValueFromPipeline=$True)]
[ValidateNotNullorEmpty()]
[alias("name")]
[string]$Mail,
[Parameter(Position=1,Mandatory=$True)]
[ValidateNotNullorEmpty()]
[string]$Password,
[ValidateNotNullorEmpty()]
[string]$Uri = "http://chi-ex01.globomantics.local/owa"
 
)
 
Process {
 
$username = $mail
 
Write-Verbose "Invoke web request OWA logon via $uri for $username"
 
$r = Invoke-WebRequest $uri -SessionVariable owa
$form = $r.Forms[0]
 
$Form.fields.username= $username
$form.Fields.password= $password
 
$authpath = "$uri/auth/owaauth.dll"
 
$r = Invoke-WebRequest -Uri $authpath -WebSession $owa -Method POST -Body $form.Fields
 
Write-Verbose $r.StatusDescription
 
 
#fill out language form
if ($r.forms[0].id -eq "lngfrm") {
Write-verbose "First time logon"
 
$form = $r.Forms[0]
 
#add fields to the form using the default values
$form.Fields.add("lcid",$r.ParsedHtml.getElementById("selLng").value)
$form.Fields.add("tzid",$r.ParsedHtml.getElementById("selTZ").value)
 
$langpath = "$uri/lang.owa"
$r = Invoke-WebRequest -Uri $langpath -WebSession $owa -Method $form.Method -Body $form.fields
 
write-verbose $r.Statusdescription
 
} #if
elseif ($r.forms[0].id -eq "logonform") {
#we got the logon form again so something went wrong
Write-Warning "Failed to logon $username. Check the password or account."
}
} #process
 
} #end function


To use this, I get all my mail-enabled user accounts using the Active Directory module. Remember, everything I’m doing is on the client. In this case, it’s Windows 8 with RSAT installed.

$OUPath = "OU=Employees,DC=globomantics,DC=local"
$users = get-aduser -filter {mail -like "*" -AND enabled -eq $True} -properties mail -SearchBase $OUPath -ErrorAction Stop

I could use all the accounts, but I prefer to only update a subset at a time.

$randomusers = $users | get-random -count 25

Finally, I use my function for each user account, pausing for a random period of 5 to 30 seconds between each one.

foreach ($user in $randomusers) {
write-host "$(Get-Date) Logging on $($user.mail)" -fore Green
 
Set-OWALogon $user.mail "P@ssw0rd"
 
Start-sleep -Seconds (Get-random -min 5 -max 30)
}

And that’s it. Now when I look at user data on the Exchange server I have something to look at!
Exchange 2010 powershell Last Logon
I don’t worry about closing the web session, as any resource use I feel helps the simulation. In fact, running this code is useful if I want to generate activity that will show up in performance data. Obviously it is not the same as having 100 real users pounding away on the Exchange server. But most of the time I’m just testing techniques; the actual data doesn’t matter as long as there is something. In the next article, I’ll share another method of generating test Exchange data.