Question about PowerShell GUI development

I’m experienced with PowerShell but not with GUI development.
I’ve put together a script that reaches out to a JEA PowerShell remote end point on a PDC emulator domain controller, retrieves certain EventLog events, parses them to identify locked out AD accounts and provide details like when account locked and the computer name where the bad password attempts came from.

Now, the task is to put a GUI on that.
I put together a form like

$FormWidth = 800
Add-Type -assembly System.Windows.Forms

# Main form
$main_form = New-Object System.Windows.Forms.Form
$main_form.Text ='View locked-out accounts in xxx AD domain'
$main_form.Width = $FormWidth
$main_form.Height = 550
$main_form.AutoSize = $true

# Messaging Label Element:
$MessageLabel = New-Object System.Windows.Forms.Label
$MessageLabel.Location = New-Object System.Drawing.Point(10,10)
$MessageLabel.MaximumSize = New-Object System.Drawing.Size(($FormWidth-20),0) # Word-Wraps text in Label
$MessageLabel.AutoSize = $true
$main_form.Controls.Add($MessageLabel)

# Irrelevent code removed..

$MessageLabel.Text = "Successfully established remote connection to server $JEAServernn"

$MessageLabel.Text += "Scanning Security Event Logs on $JEAServer for event 4740 (Account Locked-out)nn"

# Irrelevent code removed..

# Scan label:
$ScanLabel = New-Object System.Windows.Forms.Label
$ScanLabel.Location = New-Object System.Drawing.Point(10,120)
$ScanLabel.MaximumSize = New-Object System.Drawing.Size(($FormWidth-20),0) # Word-Wraps text in Label
$ScanLabel.AutoSize = $true
$main_form.Controls.Add($ScanLabel)

$ScanLabel.Text = "$(Get-Date -format 'dd MMMM yyyy hh:mm:ss tt'): Identified the following locked-out user accounts:nn"

# Sample $EventList:
$EventList = @(
[PSCustomObject]@{User = 'abc'; DC = 'myDC1'; TimeStamp = ((Get-Date).AddMinutes(-5)); SourceComputer = 'xyz'}
[PSCustomObject]@{User = 'bcd'; DC = 'myDC1'; TimeStamp = ((Get-Date).AddMinutes(-15)); SourceComputer = 'klm'}
)

if ($EventList) {
$ScanLabel.Text = "$(Get-Date -format 'dd MMMM yyyy hh:mm:ss tt'): Identified the following locked-out user accounts:nn"

# Present Data in a DataGridView
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size= "$($FormWidth-20),300"
$dataGridView.Location = "10,160"
$dataGridView.AllowUserToAddRows = $false
$dataGridView.ColumnCount = 4
$dataGridView.Columns[0].Name = "User"
$dataGridView.Columns[1].Name = "DC"
$dataGridView.Columns[2].Name = "TimeStamp"
$dataGridView.Columns[3].Name = "SourceComputer"
foreach ($Event in $EventList) {
$dataGridView.Rows.Add($Event.User, $Event.DC, $Event.TimeStamp, $Event.SourceComputer)
}
$dataGridView.AutoSizeColumnsMode = 10 # https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.datagridviewautosizecolumnsmode
$dataGridView.AutoSizeRowsMode = 10
$main_form.Controls.Add($dataGridView)

} else {
$ScanLabel.Text += "$(Get-Date -format 'dd MMMM yyyy hh:mm:ss tt'): No Locked-out accounts found`n"
}

$main_form.ShowDialog()

With output similar to:


Question:

  • Generally, how do I make an element; such as a label appear on the form without having to wait to the end of the script?
    ‘$main_form.ShowDialog()’ seems to stop the script from processing further. That’s why I have it all the way at the end.
    For example, if I add ‘$main_form.ShowDialog()’ at line 24, I will have to close the form interactively for the script to proceed…

The approach I chose was to build the Windows Form in a function setting all the object scopes to Script. Maybe not the best way, but it works for me. In your function, you have this:

$Script:ScanLabel = New-Object System.Windows.Forms.Label
$ScanLabel.Location = New-Object System.Drawing.Point(10,120)
$ScanLabel.MaximumSize = New-Object System.Drawing.Size(($FormWidth-20),0) # Word-Wraps text in Label
$ScanLabel.AutoSize = $true

Then, in the rest of the script, you simply refer to the label as such

$ScanLabel.Text = ‘I want to change the label text to something’

You can find the properties for all your form objects online and reference them anywhere in your script.

You can close the form with $main_form.Close()