Azure – resetting a password for a Domain Controller VM

I came across a situation the other day.

In my Azure Tenant, I have a VM, a domain controller that hosts, well… my domain.

I only use it for testing, most recently I was doing some SSPR testing. I only turn it on occasionally for testing some powershell scripts, this password reset utility, and other things that only an on-premises Domain Controller can really do.

Over time, over about 2 weeks I didn’t need it and had this server sitting in a powered off state. When I did need it again, after powering it on, I realized I couldn’t login with my Domain Admin credentials. The error was that my password had expired, and I needed to reset it.

Okay, I’ll use my backup Domain Admin account to reset it. The problem was, the backup Domain Admin account was giving the same error.

Uh-oh.

My primary, and backup domain admin accounts to my one cloud controller that is not replicated anywhere are both locked out. Now what?

Luck has it, there’s as way to do this that’s fairly painless and actually quite simple.

  1. Create a .ps1 file. The only contents it needs are one line:
Net user AD-Admin NewP@ssword!

Name it something relevant like “password_reset.ps1”

This HAS to be an account that’s active in your AD, and perferrably a Domain Admin account. The password can be whatever you want, as long as it fits your password domain policy.

2. Goto portal.azure.com -> Storage accounts -> any_of_your_storage_accounts ->containers (create one if you have to) -> upload. Upload the .ps1 file you created in step 1 above.

3. In portal.azure.com -> Virtual Machines -> Your_VM_DC -> Settings -> Extensions + Applications -> Add a Custom Script Extension

Browse to the storage container in step 2, point to the .ps1 file created in step 1

Let the deployment run

6. Log onto your DC VM in Azure with the credentials from step 1 above. RESET any or all your domain admin passwords that have that requirement.

7. Uninstall and delete that Custom script extension from step 3 for this VM. Otherwise, every time it boots it will reset the password for this one user.

Delete that .ps1 file from the storage container too!

Azure – Import users into cloud via CSV file

There’s a few different methods to import users into your Azure tenant.

  1. In the Azure Active Directory Portal https://aad.portal.azure.com -> Users -> Bulk Operations -> Bulk create
  2. Or you can use a little powershell

This will focus on the powershell method. Mainly because the Azure Portal only requires point and click. Plus, this is way more fun.

The Sample CSV format:

UserPrincipalNameDisplayNameGivenNameSurnamejobTitleMailNickNameObjectIdAccountEnabledAgeGroupCityCompanyNameConsentProvidedForMinorCountryCreationTypeDepartmentFacsimileTelephoneNumberIsCompromisedImmutableIdMobilePasswordPoliciesPasswordProfilePhysicalDeliveryOfficeNamePostalCodePreferredLanguageShowInAddressListStateStreetAddressTelephoneNumberUsageLocationUserStateUserStateChangedOnUserType
nabendun@customdomain.onmicrosoft.comNabendu NahasapeemapetilonNabenduNahasapeemapetilon$null104667339TrueMinorSpringfieldnull United States null856-511-6827    304-960-7231    Guder Lao2810Nepali Illinois43090 Jay Drive314-812-4954US      Member
jimboj@customdomain.onmicrosoft.comJimbo JonesJimboJones$null142259518TrueMinorSpringfieldnull United States null546-298-0636    558-695-5632    Purabaya2810Hebrew Illinois39176 Weeping Birch Court851-166-3492US      Member

And here’s the sample code below.

Make sure before you run to execute connect-azureAD first!

$CSV = Import-Csv C:\path_to_CSV_file.csv -Delimiter ","
 
foreach ($User in $CSV) {
$user.UserPrincipalName
$user.DisplayName
$user.GivenName
$user.Surname
$user.jobTitle
$user.MailNickName
$user.ObjectId
$user.AccountEnabled
$user.AgeGroup
$user.City
$user.CompanyName
$user.ConsentProvidedForMinor
$user.Country
$user.CreationType
$user.Department
$user.FacsimileTelephoneNumber
$user.IsCompromised
$user.ImmutableId
$user.Mobile
$user.PasswordPolicies
$user.PasswordProfile
$user.PhysicalDeliveryOfficeName
$user.PostalCode
$user.PreferredLanguage
$user.ShowInAddressList
$user.State
$user.StreetAddress
$user.TelephoneNumber
$user.UsageLocation
$user.UserState
$user.UserStateChangedOn
$user.UserType

         Set-AzureADUser -ObjectID $user.UserPrincipalName `
         -jobTitle $User.jobtitle `
         -AgeGroup $User.AgeGroup `
         -City $User.City `
         -CompanyName $User.CompanyName `
         -Country $User.Country `
         -Department $User.Department `
         -FacsimileTelephoneNumber $user.FacsimileTelephoneNumber `
         -Mobile $User.Mobile `
         -PhysicalDeliveryOfficeName $user.PhysicalDeliveryOfficeName `
         -Postalcode $user.PostalCode `
         -State $user.state `
         -Streetaddress $user.StreetAddress `
         -TelephoneNumber $user.TelephoneNumber `
         -UsageLocation $user.UsageLocation

        write-output $User
}

PowerShell – Adding Proxy Addresses by CSV

This is going to be a little different. As per usual, we need to follow our regular set of steps when dealing with a large amount of data that needs validation.

  1. Export list of users into CSV format
  2. Add new values into CSV
  3. Import CSV list with values
  1. Export list of users into CSV format
get-aduser -filter * -properties samaccountname | select samaccountname,mail | Export-Csv "C:\Users_add_proxy_addresses.csv"

2. Edit the CSV with the proxy email addresses you want. The format you need is the accountname (samaccountname), and proxyaddresses (SMTP:proxyemail@email.com). Like so below:

samaccountnameproxyaddresses
rick.sanchezSMTP:rick.sanchez@newproxyaddress.com
rick.richardsonSMTP:rick.richardson@newproxyaddress.com
Codie.YoutheadSMTP:Codie.Youthead@newproxyaddress.com
You NEED that “SMTP:” portion in front, otherwise it won’t take.

3. Import the .CSV file with some code:

Import-module ActiveDirectory

$Imported_csv = Import-Csv -Path "C:\Users_add_proxy_addresses.csv"
foreach ($user in $Imported_csv)
{
    $User.samaccountname
    $User.proxyaddresses
    Set-ADUser -Identity $User.samaccountname -Add @{proxyAddresses= $User.proxyaddresses}
}
$total = ($Imported_csv).count
$total
write-host "AD accounts added with proxy addresses..."

Or , if you want to add a certain SMTP extension use this code from a SAMaccountname CSV file for all users:

$Imported_csv = Import-Csv -Path "C:\Users_add_proxy_addresses.csv"
foreach ($user in $Imported_csv)
{
    $User.samaccountname
    Set-ADUser -Identity $User.samaccountname -Add @{proxyAddresses= "SMTP:" + $User.samaccountname + "@newproxyaddress.com"}
}
$total = ($Imported_csv).count
$total
write-host "AD accounts added with proxy addresses..."

Make sure to check your work:
Get-ADUser -Filter * -Properties SamAccountname, proxyAddresses | where proxyAddresses -ne $null | select-object samaccountname,proxyaddresses | out-gridview

The above only shows ONE proxy address at a time. Since the attribute proxy-address can actually store more than one value, it’s an array.

Showing the results

The Get-ADUser cmdlet does the job nicely. Although, it’s not quite as neat as I would like:

get-aduser -filter * -properties samaccountname, proxyaddresses | Select-object samAccountName, proxyaddresses | Out-GridView

A sample output below shows the results of the proxyaddress attributes. Notice how all the different proxies are put together in the same column.

This is OK, and does require some finer tweaking with a CSV editor. However, there’s got to be a way to display each proxy address independently in their own column.

I did a little searching and I found this from the devblogs microsoft guys:

Get-ADUser -Filter * -Properties proxyaddresses | select samaccountname, @{L='ProxyAddress_1'; E={$_.proxyaddresses[0]}}, @{L=’ProxyAddress_2';E={$_.ProxyAddresses[1]}} | out-gridview

This lists out the proxy addresses by column with some help from the select statement above.

[ivory-search 404 "The search form 3350 does not exist"]

PowerShell – Changing Departments for Multiple AD Users

Hostile takeover? All employees of a department being reassigned? We won’t go into ‘how to disable way lots of employees because your upper management said ‘because we told you”. So, we’ll go into changing departments for the entire company.

There’s a few different ways to do this:

  1. Exporting to CSV, making absolutely sure who’s in the list.
  2. Or just changing everyone in one department, and replacing it with an entirely different department name.

Typically, you want option 1. This is fact checking, validation, all that stuff.

In that scenario you follow the same sort of methodolgy:

-Export all users that meet the criteria (in this case, everyone of a certain department) into a CSV file

-Take CSV file, inject into powershell and set new value

There is also a ‘once and done’ approach. Where you can simply replace the values in one string. I don’t suggest this for production environments, namely because there’s always a margin for error.

The ‘typical’ option

Export all users that fit a filter into a CSV file. In this example, I’m looking for all AD users with Department ‘Support’. Exporting to a CSV file. Export something unique, like the samAccountName.

Get-ADUser -Filter 'Department -like "Support"' -Properties * | select samaccountname | Export-CSV "C:\Path_to_csv\department_users.csv"

With this exported file “department_users.csv”, we use another piece of code to pick up the CSV file, run a for-loop to go through each user in that CSV file and update their department.

Import-Module Active Directory

$Set_Department = Import-Csv "C:\Path_to_csv\department_users.csv"  #Store CSV file into $Set_Department variable
$Department = "Operations Support" #New Department

foreach ($User in $Set_Department) {
    #For each name or account in the CSV file $Resetpassword, reset the password with the Set-ADAccountPassword string below
   $User.sAMAccountName

        Set-ADUser -Identity $User.sAMAccountName -Department $Department
        write-output $User
}
 Write-Host " Departments changed "
 $total = ($Set_Department).count
 $total
 Write-Host "AD User Departments have been updated..."

Just make sure your end CSV file has a format of only the SamAccountname (like below).

sAMAccountName
Zuzana.Burris
Zandra.Caig

The ‘Once-and-Done’ Option

Again, I don’t suggest this unless you feel absolutely comfortable with the results. If, however you’re in a hurry and need to change all attributes to the new updated attribute, this is the line of code for you.

Get-ADUser -Filter 'Department -like "Old Department Name"' | Set-ADUser -replace @{department="New Department Name"}

As always, you can retrofit this code to suit your needs.

You could also change other AD attributes with this sort of syntax as well, just be sure to change your code, and TEST first.

Showing the Results

Let’s see which AD Accounts by SamAccountName, Department and Title have a specific title. We’ll say anything with a title of “Support”.

Get-ADUser -Filter 'Department -like "*Support*"' -Properties * | select samaccountname, department, title | out-gridview

Or you could search for all users that do NOT have a department specified

Get-ADUser -Filter * -Properties * | select samaccountname, Department,title | where department -eq $Null

PowerShell – Change passwords on multiple AD accounts

If you’re like me, you built a new AD for testing. And if you’re also like me, you imported a whole bunch of users into your AD. Some of those users likely had passwords that didn’t quite meet the domain criteria. If that happened, that means those users are disabled.

In past posts, I wrote about moving users into a different OU. Now, we’re going to change passwords for these users so we can enable them later.

For this, you’ll need a CSV in this format below. I took some liberties and retrieved a large amount of random passwords from manytools.org. There are many websites that can do this, I just like the format that manytools provided. I just pasted the passwords into the second column, the first column being the sAMAccountname of the user.

sAMAccountNamePassword
Zuzana.Burrisgz9DndwkBh8s
Zandra.Caig9eC3bcJ2SzA5

PowerShell code:

Import-Module Active Directory
$Resetpassword = Import-Csv "C:\path_to_username_password_file.csv"
 #Store CSV file into $Resetpassword variable

foreach ($User in $Resetpassword) {
    #For each name or account in the CSV file $Resetpassword, reset the password with the Set-ADAccountPassword string below
    $User.sAMAccountName
    $User.Password
        Set-ADAccountPassword -Identity $User.sAMAccountName -Reset -NewPassword (ConvertTo-SecureString $User.Password -AsPlainText -force)
}
 Write-Host " Passwords changed "
 $total = ($Resetpassword).count
 $total
 Write-Host "Accounts passwords have been reset..."

Showing the Results

Once we’ve set the passwords, we need a way of knowing when or if the passwords were last set for a user. Referring back to the Get-ADuser cmdlet, we can look at the -passwordlastset property.

Get-ADUser -filter * -properties passwordlastset | sort-object samaccountname | select-object samaccountname, passwordlastset, passwordneverexpires | Out-GridView

PowerShell – Finding all Disabled users in AD

Need to find all the disabled users in your AD? it’s odd that the built in AD Tools do not have this option. PowerShell to the rescue!

All these commands are documented in the Microsoft Get-ADUser cmdlet. I’ve added some additional types of output with out-gridview and CSV.

Finds all the disabled users in AD.

Get-ADUser -Filter {Enabled -eq $false}

Finds all and outputs to a gridview for editing (but you need excel)

Get-ADUser -Filter {Enabled -eq $false} | Out-GridView

Finds all disabled users and outputs to a CSV file. This exports ALL the readily available attributes.

Get-ADUser -Filter {Enabled -eq $false} | export-csv "C:\users\Administrator\Desktop\disabled_ADusers.csv"

Finds all the disabled users by specific property and selects the objects and outputs them

Get-ADUser -Filter {Enabled -eq $false} -properties SamAccountName,mail | Select-Object SamAccountName,mail | Out-GridView

Finds all the disabled users, looks a properties that aren’t readily available like email, export to a CSV file

For some reason, you can’t search by mail address outright or have it display, you have to select it, then show it with that select-object command

Get-ADUser -Filter {Enabled -eq $False} -Properties SamAccountName,mail | Select-object SamAccountName,mail| export-csv "C:\Path_to_CSV.csv"

Or you can use variables to clean up the code:

$SamAcc = "SamAccountName"
$Desc = "Description"
$mail = "mail"
$title = "title"
$CSVpath = "C:\Path_to_CSV.csv"

Get-ADUser -Filter {Enabled -eq $False} -Properties $SamAcc,$mail | Select-object $SamAcc,$mail| export-csv $CSVpath
[ivory-search 404 "The search form 3350 does not exist"]

PowerShell – Importing AD users from CSV

What are the situations you’ll need lots of Dummy AD data? When you want to run some awesomely crafted PowerShell AD scripts, that’s where.

I was in a situation a few months ago where I needed replicate a VERY large set of AD data in order to pull some queries with Get-Aduser and Set-Aduser. I toyed with the idea of just creating them manually, or restoring them from production (this is a bad idea, don’t restore from production for a testing environment!). So I found a script that would import a great deal of AD data, and I also found a place online that can create a buttload of fake AD data for me.

The Setup:

  • A Running AD Domain controller
  • Powershell ISE
  • https://mockaroo.com CSV loaded with all sorts of dummy AD data

First, let’s hit mockaroo.com.

For this entry, I’m going to fill as many fields as possible that relate to a powershell script. For each field in powershell, we’ll create a matching one in Mockaroo, and we’ll name it consistently.

This is the set of data with parameters I’m using:

The fields I’m using in AD are (copied from above)

  • City
  • Company
  • Description
  • Department
  • Email
  • EmployeeID
  • GivenName
  • MobilePhone
  • Office
  • OfficePhone
  • Password
  • Path
  • Postalcode
  • State
  • Streetaddress
  • Surname
  • Title

I also need to place these created accounts into a specific OU. I had to do some editing in Excel, and placed the OU ADSI path into that Path attribute above.

Here’s my example with column and headers:

CityCompanyDepartmentEmailEmployeeIDGivenNameMobilePhoneOfficeOfficePhonePasswordPathPostalcodeStateStreetAddressSurNameTitle
IpabaSkybleHuman Resourcescyouthead0@google.es873-02-1259Codie+55 283 153 2678 592-719-1607MhyWOJIAE“OU=Contoso-Users,DC=Contoso,DC=com”35198-000 787 Arizona TrailYoutheadStructural Analysis Engineer

Generate the data you need, or create the information you require and save it into a CSV file (ideally in a Windows format).

Now, the powershell code:

01	# Import active directory module for running AD cmdlets
02	Import-Module activedirectory
03	 
04	#Store the data from ADUsers.csv in the $ADUsers variable
05	$ADUsers = Import-csv "C:\Users\Administrator\Desktop\2AD_users.csv"
06	 
07	#Loop through each row containing user details in the CSV file 
08	foreach ($User in $ADUsers)
09	{
10	#Read user data from each field in each row and assign the data to a variable as below
11	$City           =$User.City
12	$Company        =$User.Company
13	$Description    =$User.Description
14	$Department     =$User.Department
15	$Email          =$User.Email
16	$Employeeid     =$User.Employeeid
17	$GivenName      =$User.GivenName
18	$MobilePhone    =$User.MobilePhone
19	$Office         =$User.Office
20	$OfficePhone    =$User.OfficePhone
21	$Password       =$User.Password
22	$Path           =$User.Path
23	$Postalcode     =$User.Postalcode
24	$State          =$User.State
25	$Streetaddress  =$User.Streetaddress
26	$SurName        =$User.SurName
27	$Title          =$User.Title
28	$Username       =$GivenName+"."+$Surname
29	 
30	    #Check to see if the user already exists in AD
31	if (Get-ADUser -F {SamAccountName -eq $Username})
32	    {    #If user does exist, give a warning
33	         Write-Warning "A user account with username $Username already exist in AD."
34	    }
35	    else
36	    {
37	        #User does not exist then proceed to create the new user account
38	        #Account will be created in the OU provided by the $Path variable read from the CSV file
39	        New-ADUser `
40	            -City $City `
41	            -Company $Company `
42	            -Description $Description `
43	            -Department $Department `
44	            -Email $Email `
45	            -EmployeeID $EmployeeID `
46	            -GivenName $GivenName `
47	            -MobilePhone $MobilePhone `
48	            -Office $Office `
49	            -OfficePhone $OfficePhone `
50	            -AccountPassword (convertto-securestring $Password -AsPlainText -Force) `
51	            -Path $Path `
52	            -Postalcode $Postalcode `
53	            -State $State `
54	            -Streetaddress $Streetaddress `
55	            -SurName $SurName `
56	            -Title $Title `
57	            -SamAccountName $Username `
58	            -UserPrincipalName "$Username@dtab.org" `
59	            -Name "$GivenName $SurName" `
60	            -DisplayName "$GivenName $SurName" `
61	            -Enabled $true
62	    }
63	}

I had to reference the New-ADUser powershell commandlet. A great deal of the switches came from this article. As you can see, I’ve also alphabetized the fields to make sorting a little easier.Notes about this script:

Code Syntax explanation – lines 11 – 28

01	$City           =$User.City
02	$Company        =$User.Company
03	$Description    =$User.Description
04	$Department     =$User.Department
05	$Email          =$User.Email
06	$Employeeid     =$User.Employeeid
07	$GivenName      =$User.GivenName
08	$MobilePhone    =$User.MobilePhone
09	$Office         =$User.Office
10	$OfficePhone    =$User.OfficePhone
11	$Password       =$User.Password
12	$Path           =$User.Path
13	$Postalcode     =$User.Postalcode
14	$State          =$User.State
15	$Streetaddress  =$User.Streetaddress
16	$SurName        =$User.SurName
17	$Title          =$User.Title
18	$Username       =$GivenName+"."+$Surname

This code stores the CSV columns (headers) information for each user into $variables. I’ve kept the variable names the same as the CSV headers tokeep things simple. The last line, $username stores the firstname (period) lastname. Everyone will likely have a different standard for usernames, I just like this format since I don’t have to re-write code.

Code Syntax explanation – lines 31-33

1	if (Get-ADUser -F {SamAccountName -eq $Username})
2	    {    #If user does exist, give a warning
3	         Write-Warning "A user account with username $Username already exist in AD."

Some error checking code. Checking for duplicate information in the imported CSV file.

The variables used in this script are considered ‘more than necessary’. The bare basic amount needed (I think) are username, password, first and last name. Anything you add or take away, requires some editing of the code, and the CSV file.

Notes about this Script:

  • For some reason the New-ADUser command does not allow the -country variable, it seems to fail each time. This is not a deal breaker for me, but for you it might be something worth investigating.
  • When importing passwords, use 10 characters at a minimum. Mockaroo only shows a password “between 6 to 12 chars”. By trial and error, I found manytools.org where I could generate the passwords necessary in the format I wanted, and copy/pasted them into my user CSV.
  • Should you feel hesitant about running this script, add “-Whatif” on the end of the Add-ADUser cmdlet line

Creating your first Powershell GUI – Part 3

Getting caught up?

  • Part 1 – Creating a simple GUI
  • Part 2 – Adding variables, added functionality
  • Part 3 – (that’s where we are)

In this post, I’ll be adding some error checking in the form of a function. In a late article, I may revisit this and re-write some of these functions to clean up the code.

For now, we’ll focus on the following:

The Form should look something similar based on the code provided in Part 2

In Part 2, we used

  • Textbox1
  • Button1
  • Datagridview1

I’ll be covering element 3 (Button2) – “Search output to out-gridview” in this post. I’ll also be doing some error-checking in the event that there are no results to display.

In Part 2, we used this piece of code to output the results of the query into the datagridview:

Function ADSearch {
$wc = ‘*’ 
$Namesearch = $wc + $textbox2.text + $wc 
$results= get-Aduser -Filter {name -like $Namesearch} -Server $Server -Credential $Credential 
$DataGridView1.DataSource = [System.Collections.ArrayList]([System.Object[]](,$results | Sort-Object SamAccountName, Name))
}

Now, we’re going to configure that button to use the powershell utility out-gridview. If you’re not familiar with this command, it’s super handy for many reasons.

Below is the code, with the highlighted portions that pertain to the functionality of Button2, and  function ADSearch2.

$results = $null #reset the $results variable each run 
$server = "10.0.0.101"
$Credential = "dtab\administrator"

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

$Form                            = New-Object system.Windows.Forms.Form
$Form.ClientSize                 = New-Object System.Drawing.Point(400,389)
$Form.text                       = "Form"
$Form.TopMost                    = $false

$Button1                         = New-Object system.Windows.Forms.Button
$Button1.text                    = "Search for User"
$Button1.width                   = 124
$Button1.height                  = 30
$Button1.location                = New-Object System.Drawing.Point(41,52)
$Button1.Font                    = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
<#-----Calls a function 'ADSearch'-------#>
$Button1.Add_MouseClick({ADSearch($Textbox2.text)}) 

$TextBox2                        = New-Object system.Windows.Forms.TextBox
$TextBox2.multiline              = $false
$TextBox2.text                   = "Enter User to search here"
$TextBox2.width                  = 247
$TextBox2.height                 = 20
$TextBox2.location               = New-Object System.Drawing.Point(91,23)
$TextBox2.Font                   = New-Object System.Drawing.Font('Microsoft Sans Serif',10)

$DataGridView1                   = New-Object system.Windows.Forms.DataGridView
$DataGridView1.width             = 376
$DataGridView1.height            = 282
$DataGridView1.location          = New-Object System.Drawing.Point(12,94)

$Button2                         = New-Object system.Windows.Forms.Button
$Button2.text                    = "Search output to out-gridview"
$Button2.width                   = 206
$Button2.height                  = 30
$Button2.location                = New-Object System.Drawing.Point(182,52)
$Button2.Font                    = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Button2.Add_MouseClick({ADSearch2($Textbox2.text)}) 

$Form.controls.AddRange(@(
        $TextBox2
        $Button1,
        $Button2,
        $DataGridView1
        ))

[system.windows.forms.application]::run($form) #Runs form with runtimes - this is a trial and error thing

Function ADSearch {
        <#------function 'ADSearch' runs Get-ADUser query, stores into $results variable------#>
        $wc = '*' #Wildcard character to assist with the text search
        $Namesearch = $wc + $textbox2.text + $wc #appends the wildcard characters to the search, infront and behind, stores into the $NameSearch Variable
        $results= get-Aduser -Filter {name -like $Namesearch} -Server $Server -Credential $Credential #This is the Query itself

        $number = $results.count #Count the number of results found
        display_results($results) #call function with results found
}

Function ADSearch2{
        $wc = '*' #Wildcard character to assist with the text search
        $Namesearch = $wc + $textbox2.text + $wc #appends the wildcard characters to the search, infront and behind, stores into the $NameSearch Variable
        $results= get-Aduser -Filter {name -like $Namesearch} -Server $Server -Credential $Credential #This is the Query itself

        $number = $results.count #Count the number of results found
        display_results2($results) #call function with results found
}

function display_results{
        if ($results -ne $null){
            #if results found, display in out-grid
            $DataGridView1.DataSource = [System.Collections.ArrayList]([System.Object[]](,$results | Sort-Object SamAccountName, Name))
        }
        else{
            #if no results found, display error message
            [System.Windows.MessageBox]::Show('No results found')
        }
}

function display_results2{
        if ($results -ne $null){
            #if results found, display in out-grid
            $results | out-gridview
        }
        else{
            #if no results found, display error message
            [System.Windows.MessageBox]::Show('No results found')
        }
}

Line 41, this calls the ADSearch2 function, with the input from textbox2.

$Button2.Add_MouseClick({ADSearch2($Textbox2.text)})

Lines 62 – 69, this is the ADSearch2 function. Notice though, how it only captures it into the $result variable. We also count the number of results found and store that into $Number. This is where error-checking comes in.

Function ADSearch2{
        $wc = '*' #Wildcard character to assist with the text search
        $Namesearch = $wc + $textbox2.text + $wc #appends the wildcard characters to the search, infront and behind, stores into the $NameSearch Variable
        $results= get-Aduser -Filter {name -like $Namesearch} -Server $Server -Credential $Credential #This is the Query itself
 
        $number = $results.count #Count the number of results found
        display_results2($results) #call function with results found
}

Lines 82 – 90, is the function that checks the amount of results and either shows the results, or displays ‘No results found’ in a popup box. That ‘if’ statement below looks at the number of results. If it’s Zero, it’s a Null Value. If it’s a Null Value, it goes into the ‘Else’ portion.

function display_results2{
        if ($results -ne $null){
            #if results found, display in out-grid
            $results | out-gridview
        }
        else{
            #if no results found, display error message
            [System.Windows.MessageBox]::Show('No results found')
        }
}

This line does the output into that out-gridview I mentioned earlier.

    $results | out-gridview

The out-gridview displays into it’s own separate window. Now, the beauty of this window is that it can be resized, filtered, copied and sorted.

Now, this code is not complete by any means. But it’s working and suits our needs for now. Ideally, the functions can be rewritten with more efficient code, perhaps a case statement. But for now, we have a working GUI that can do a simple search of users within an AD environment, output to an embedded Grid, or output to an external powershell utility in gridviewer. If there’s any improvements, I’m certainly open to doing more, or seeing what the I.T Blogging community has in store.

[ivory-search 404 "The search form 3350 does not exist"]

Creating your first Powershell GUI – Part 2

Already seen this part? Check out Part 1, Part 2, and Part3

In part 1, we covered the very basics. A one-line Get-ADUser query, a simple form with one button and output textbox.

In this entry, we’ll add more functionality that:

  • Captures input from the GUI
  • Outputs specific information
  • Makes use of variables

If you remember, we started off with this form:

Simple winform with a button, a textbox, and a function.

Now, we’re going to take input from a user, and display the output to a datagridview instead of a textbox. A textbox is usually fine for output, but with a Get-ADUser query, there are multiple fields we usually want to look at. Text boxes don’t do text wrapping very well, and the output is kind of messy. Also in using a grid, it neatly sorts everything based on some criteria we create.

Here’s a sample of what the box looks like afterwards in PoshGUI:

I’ve labelled each major component, and listing the names in the Powershell code for easier reference:

  1. Textbox1 = This is the input from the end user. Or more accurately, we’ll be capturing the ‘text’, in the form of $textbox1.text into a variable, and using that in our Get-ADUser search parameter.
  2. Button1 = This executes the search query with the captured text from textbox1. The output from this will go into the 4th component: the datagridview.
  3. Button2 = This will execute the exact same search, but outputs it to an external gridviewer. I’ll go through some of the pro’s and con’s of this method later.
  4. Datagridview1 = output of Get-Aduser shows up here.

As I stated earlier, a textbox is usually fine for some simple output. A datagridview is handy since it puts all the results into columns that are resizable, sortable and we can copy directly from it. I listed two different ways to use grid form with Button 2 and the Datagridview1. I’ll cover more about what each does soon.

Below is the completed source code that:

  • Makes use of the “search for User” button (AKA, ‘$button1’)
  • Takes input from the textbox (AKA, ‘$Textbox2.text’)
  • Uses a function ‘ADSearch’ which we defined the parameters
  • Ouputs the result to a gridview embedded into the form.

I’ll go into more detail about the Gridviewer output, the variables, then we’ll create some code for error checking.

$results = $null #reset the $results variable each run
$server = “10.0.0.101”
$Credential = “contoso.com\administrator”

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(400,389)
$Form.text = “Form”
$Form.TopMost = $false

$Button1 = New-Object system.Windows.Forms.Button
$Button1.text = “Search for User”
$Button1.width = 124
$Button1.height = 30
$Button1.location = New-Object System.Drawing.Point(41,52)
$Button1.Font = New-Object System.Drawing.Font(‘Microsoft Sans Serif’,10)
<#—–Calls a function ‘ADSearch’——-#>
$Button1.Add_MouseClick({ADSearch($Textbox2.text)})

$TextBox2 = New-Object system.Windows.Forms.TextBox
$TextBox2.multiline = $false
$TextBox2.text = “Enter User to search here”
$TextBox2.width = 247
$TextBox2.height = 20
$TextBox2.location = New-Object System.Drawing.Point(91,23)
$TextBox2.Font = New-Object System.Drawing.Font(‘Microsoft Sans Serif’,10)

$DataGridView1 = New-Object system.Windows.Forms.DataGridView
$DataGridView1.width = 376
$DataGridView1.height = 282
$DataGridView1.location = New-Object System.Drawing.Point(12,94)

$Button2 = New-Object system.Windows.Forms.Button
$Button2.text = “Search output to out-gridview”
$Button2.width = 206
$Button2.height = 30
$Button2.location = New-Object System.Drawing.Point(182,52)
$Button2.Font = New-Object System.Drawing.Font(‘Microsoft Sans Serif’,10)

$Form.controls.AddRange(@(
			$TextBox2
			$Button1,
			$Button2,
			$DataGridView1
))

[system.windows.forms.application]::run($form) #Runs form with runtimes – this is a trial and error thing

Function ADSearch {
<#——function ‘ADSearch’ runs Get-ADUser query, stores into $results variable——#>
$wc = ‘*’ #Wildcard character to assist with the text search
$Namesearch = $wc + $textbox2.text + $wc #appends the wildcard characters to the search, infront and behind, stores into the $NameSearch Variable
$results= get-Aduser -Filter {name -like $Namesearch} -Server $Server -Credential $Credential #This is the Query itself
<#—–Display output to the embedded DataGridView——-#>
$DataGridView1.DataSource = [System.Collections.ArrayList]([System.Object[]](,$results | Sort-Object SamAccountName, Name))
}

Let’s look at the variables:

$results = $null #reset the $results variable each run
$server = "10.0.0.101"
$Credential = "contoso.com\administrator"

This will come in handy later when we call the Get-ADUser query. By passing variables, it becomes a little easier to change the script to fit our needs. Especially if you’re dealing with credentials or domains.

Now let’s look at the datagridview:

$DataGridView1.DataSource = [System.Collections.ArrayList]([System.Object[]](,$results | Sort-Object SamAccountName, Name))

Breaking down each component:

$DataGridView1.DataSource

This is the datagriviewd itself. By default it’s already in a neatly arranged set of columns and rows. Makes sense when we use it for a large amount of data with multiple fields. Makes for easier parsing if we’re looking for something.

[System.Collections.ArrayList]([System.Object[]]

Since the Datagrid arranges the data, we need to store the information into an array. If we didn’t, it would all be string information. Some data does not translate into string information nicely (such as ‘account enabled’, which would just translate to a 1 or a 0).

(,$results

An array by default starts with a ‘0’ (zero) value. To display the results of the AD search, we stored it into the $results variable. By putting the comma in front “,” this tells the array to display all the contents from the beginning of the array.

| Sort-Object SamAccountName, Name))

The Pipe and Sort-Object commands sort the values accordingly by SamAccountName, then by Name. These values were imported by the original get-aduser command. You could actually list this by any value that get-Aduser is able to pull, such as ‘displayname’, ’email’, etc…

Read up on Part3 of this tutorial, where I’ll be adding an error checking ability, as well as using the other button for a different style of output.

[ivory-search 404 "The search form 3350 does not exist"]

Creating your first Powershell GUI – Part 1

Already seen this part? Check out Part 1, Part 2, and Part3

Powershell, while a powerful tool can sometimes lack that person touch that a GUI interface offers. Console output is fine and dandy, but there are times when parsing through a large amount of data can be done with a GUI and some simple commands.

From here, I’m going to assume you know the basics. Such as awareness of the get-executionpolicy and set-executionpolicy commands, and a little knowledge of PowerShell ISE.

The Setup

I’m doing this with my home lab from a non-domain computer. Which means I’ll be using some credential switches you may not need.

For this example, we’ll take something simple, like

Get-ADUser

Get-ADUser gets a user object or searches to get multiple user objects from Active Directory. To keep this simple, we’ll have it search up a name. You’ll get the idea of what I’m doing and how we can keep it somewhat simple.

First, we’ll get a simple query working. Let’s search for a user named ‘Rick’

get-Aduser -Filter {name -like '*rick*' } -Server 10.0.0.101 -Credential "contoso.com\administrator"

Explanation of the above query:

-Filter {name -like '*rick*'}

Search for a username using wildcards in-front and behind of name we’re looking for.

-Server 10.0.0.101

Look at a specific Domain controller. Since this is on a different subnet for me, I’m specifying it by IP. You can simply use the hostname of the domain controller, or you can use “-SearchBase” and plug in the domain name itself.

-Credential "contoso.com\Administrator" 

Provide a specific set of credentials with read access to the domain. Since I’m issuing these commands from an non-domain machine, this is handy for me in the testing phase.

Put this or something very close to this into PowerShell ISE. Highlight the code and hit F8 to run the selection. This just ensures you have something to search for. Obviously, I chose ‘Rick’ because that name exists in my Domain, you’ll have to pick or create one to search.

So now we have the format of the query, how can we turn it into a GUI?

Visit POSHGUI.com. This is a free website that creates the Powershell forms for you. All you need to do is create the code to plunk in. Goto WinForms Designer.

Now on the form drag a button and a text box. For this example, we’ll want the text box a little bigger – which means in the ‘behavior’ section, we need multi-line. You should have something similar to the below:

I kept the default values for each. They should be button1 and textbox1 respectively from here on.

What we want to happen: When pressing ‘Search Up “Rick”‘ button, the results should show in the text box below.

In PoshGUI click on the ‘Code’ button. This creates the entire form with coordinates, arguments and ranges.

The above is a screenshot, I’ve added my code below with some additional explanation in between the comments.

$results = $null #reset the $results variable each run

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(400,324)
$Form.text = “Form”
$Form.TopMost = $false

$Button1 = New-Object system.Windows.Forms.Button
$Button1.text = “Search up `”Rick`””
$Button1.width = 253
$Button1.height = 30
$Button1.location = New-Object System.Drawing.Point(62,14)
$Button1.Font = New-Object System.Drawing.Font(‘Microsoft Sans Serif’,10)
<#—–Calls a function ‘ADSearch’——-#>
$Button1.Add_MouseClick({ADSearch})

$TextBox1 = New-Object system.Windows.Forms.TextBox
$TextBox1.multiline = $true
$TextBox1.text = “Results show up here”
$TextBox1.width = 338
$TextBox1.height = 261
$TextBox1.location = New-Object System.Drawing.Point(26,57)
$TextBox1.Font = New-Object System.Drawing.Font(‘Microsoft Sans Serif’,10)

$Form.controls.AddRange(@($Button1,$TextBox1))

[system.windows.forms.application]::run($form) #Runs form with runtimes – this is a trial and error thing

Function ADSearch {
<#——function ‘ADSearch’ runs Get-ADUser query, stores into $results variable——#>

$results= get-Aduser -Filter {name -like ‘*rick*’ } -Server 10.0.0.101 -Credential “contoso.com\administrator”

<#—–Display output to $Textbox1.text——-#>
$TextBox1.text = $results

}

Copy and paste that code, edit the query to whatever name exists in your Domain, and the IP to match whatever domain IP you have. Hit F5 to run the script, and you should see something similar.

Click the Mouse Button, enter your domain credentials and the results display:

And that’s about it! It’s a simple script, and shows some of the very simple ways to display information in a GUI. This only needs PowerShell ISE, PoshGUI, and some curiosity.

If you want to make this a little fancier, take input from a user, and display specific information, join me for Part 2.

[ivory-search 404 "The search form 3350 does not exist"]