Avatar of cawasaki
cawasaki

asked on 

modify powershell gui script i use for change user password

hello Experts,

i need your help today to modify this powershell GUI script.
i use this script for my help desk, its a gui to change a user password (active directory account)  and send the new password to the printer, then the user can use his badge to print his new password.

in this script, actually, the password need to be entred by the administrator, what i need is generate the password automaticly with 10 char (lowercase, uppercase and number).

i need also to remove some think from the gui like the picture, I surrounded what i need to remove.

thanks for help
pic.jpg
script.txt
PowershellScripting Languages

Avatar of undefined
Last Comment
cawasaki
Avatar of David Johnson, CD
David Johnson, CD
Flag of Canada image

what did you use to create this? I would use that to edit the script and change the forms AND the checks
Avatar of cawasaki
cawasaki

ASKER

Hello David,

i have get this script from here and modify it a litle bit.
what i have see that this script have been generated by: SAPIEN Technologies, Inc., PowerShell Studio.

here the original script:

https://github.com/supersysadmin/WindowsPowerShell/blob/master/Scripts/GUI/AD/ADUserResetPassword.ps1

thanks for help
Avatar of cawasaki
cawasaki

ASKER

anyone can help please?
Avatar of oBdA
oBdA

Working on something ...
Avatar of cawasaki
cawasaki

ASKER

hello oBdA,

thank you for your help, i am waiting :)
Avatar of cawasaki
cawasaki

ASKER

Hello oBdA,

any news?

thanks for help
Avatar of oBdA
oBdA

Extremely busy, but I haven't forgotten you.
Avatar of cawasaki
cawasaki

ASKER

Ok i wait 👍🏽
Avatar of cawasaki
cawasaki

ASKER

hello oBdA
any news?

thanks
Avatar of oBdA
oBdA

Turned out a bit more involved than what I originally had in mind; might turn this into an article after all ...
Anyway, try this for starters. It requires the PowerShell cmdlets on the machine(s) where it's started.
Currently, it doesn't log into a file, but you can adjust the function Write-Log easily if you need that.
You can start it with a shortcut so that the PS console stays minimized (might start the GUI in the background, though) or hidden, for example like that:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Minimized C:\PS\Reset-ADUserPassword.ps1

Open in new window

For testing, just start it from a PS console, so you can see what's happening.
The function Invoke-CustomAction at the beginning is yours to do with as you please. It will only be called after a successful password change.
Clicking "New Password" will auto-generate a new random password; depending on the $Script:enableShowPassword and $Script:enableEditPassword variables at the beginning, the password can be entered manually as well, and/or revealed.
"Unlock account" and "User must change password" are only enabled if the account flags allow for that to happen.
$Script:enableShowPassword = $true
$Script:enableEditPassword = $true
$Script:passwordLength = 10

$Script:dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
$Script:aDUserProperties = 'SamAccountName', 'DisplayName', 'DistinguishedName', 'Enabled', 'LockedOut', 'CanNotChangePassword', 'PasswordNeverExpires', 'PasswordLastSet'
$Script:myName = $MyInvocation.MyCommand.Name

Function Invoke-CustomAction {
Param($ADUser, $NewPassword)
	Write-Log -Message 'Running custom action ...'
}

Function New-RandomPassword {
Param([ValidateRange(9, 32)][Int32]$Length = 9)
	$categories = @{
		'Lower' =		'abcdefghijkmnopqrstuvwxyz'	# Lowercase l might be confused with digit 1 or uppercase i
		'Upper' =		'ABCDEFGHJKLMNPQRSTUVWXYZ'	# Uppercase i might be confused with digit 1 or lower-case L; uppercase o might be confused with digit 0
		'Digits' =		'23456789'					# 0 might be confused with uppercase o; 1 might be confused with lowercase L or uppercase i.
		'NonAlpha' =	'!#$%&()*+,-./:;<=>?@[\]_{}~'
	}
	$characterPool = $password = ''
	ForEach ($category In 'Lower', 'Upper', 'Digits') {
		1..3 | ForEach-Object {$password += $categories[$category][(Get-Random -Maximum $categories[$category].Length)]}
		$characterPool += $categories[$category]
	}
	For ($i = $password.Length; $i -lt $Length; $i++) {
		$password += $characterPool[(Get-Random -Maximum $characterPool.Length)]
	}
	Return (-join [char[]]([byte[]][char[]]$password | Get-Random -Count ([Int32]::MaxValue)))
}

Function Show-FormMain {
	Add-Type -AssemblyName System.Drawing
	Add-Type -AssemblyName System.Windows.Forms
	[System.Windows.Forms.Application]::EnableVisualStyles()

	Function Write-Log {
	Param([String]$Message, [ValidateSet('Information', 'Warning', 'Error')]$Type = 'Information')
		$ForegroundColor = Switch ($Type) {'Information' {'White'}; 'Warning' {'Yellow'}; 'Error' {'Red'}}
		Write-Host "[$(Get-Date -Format $Script:dateTimeFormat)] [$($Type)] $($Message)" -ForegroundColor $ForegroundColor
		$statusBarPanelLog.Text = $Message
	}
	
	Function Initialize-ControlState {
		$buttonFind, $buttonNewPwd, $textBoxPwd, $checkBoxShowPwd, $checkBoxUnlockAcct, $checkBoxChangePwd, $buttonOK, $buttonApply | ForEach-Object {$_.Enabled = $false}
		$textBoxSam, $textBoxUserDisplay, $textBoxUserDN, $textBoxUserFlags, $textBoxPwd | ForEach-Object {$_.Text = ''}
		$textBoxPwd.PasswordChar = '*'
		$textBoxSam.ReadOnly = $false
		$buttonFind.Text = '&Find'
		$checkBoxShowPwd, $checkBoxUnlockAcct, $checkBoxChangePwd | ForEach-Object {$_.Checked = $false}
		[void]$textBoxSam.Focus()
	}

	# region events
	$evt_Click = {
	Param($Sender, $EventArgs)
		Switch ($this.Name) {
		
			'buttonFind' {
				If ($textBoxSam.ReadOnly) {		# buttonFind has the 'Clear' function
					Initialize-ControlState
					Return
				}
				If ([string]::IsNullOrEmpty($textBoxSam.Text.Trim())) {
					Return
				}
				$samAccountName = $textBoxSam.Text
				Try {
					If (-not (Get-Module -Name ActiveDirectory)) {
						Write-Log -Message 'Loading Active Directory Module ...'
						Import-Module ActiveDirectory -ErrorAction Stop -Function Get-ADUser, Set-ADAccountPassword, Set-ADUser, Unlock-ADAccount
					}
					Write-Log -Message "Searching '$($samAccountName)' ..."
					If ($Script:aDUser = @(Get-ADUser -Filter {sAMAccountName -like $samAccountName} -Property $Script:aDUserProperties -ErrorAction Stop | Select-Object -Property $Script:aDUserProperties)) {
						If ($Script:aDUser.Count -eq 1) {
							$action = 'Found'
						} Else {
							Write-Log -Message "Found $($Script:aDUser.Count) users matching '$($samAccountName)', waiting for selection ..."
							If (-not ($Script:aDUser = $Script:aDUser | Out-GridView -Title "Multiple matches; please select" -OutputMode Single)) {
								Write-Log -Message "Operation canceled!"
								Return
							}
							$action = 'Selected'
						}
						$textBoxSam.ReadOnly = $true
						$textBoxSam.Text = $Script:aDUser.sAMAccountName
						$textBoxUserDisplay.Text = $Script:aDUser.DisplayName
						$textBoxUserDN.Text = $Script:aDUser.distinguishedName
						$accountFlags = @()
						If (-not $aDUser.Enabled)			{$accountFlags += 'DISABLED'}
						If ($aDUser.LockedOut)				{$accountFlags += 'LOCKEDOUT'}
						If ($aDUser.CanNotChangePassword)	{$accountFlags += 'PASSWD_CANT_CHANGE'}
						If ($aDUser.PasswordNeverExpires)	{$accountFlags += 'DONT_EXPIRE_PASSWORD'}
						If (-not $aDUser.PasswordLastSet)	{$accountFlags += 'PASSWD_MUST_CHANGE'}
						$textBoxUserFlags.Text = $accountFlags -join ', '
						$buttonFind.Text = 'C&lear'
						$buttonNewPwd, $textBoxPwd, $checkBoxShowPwd, $buttonOK, $buttonApply | ForEach-Object {$_.Enabled = $true}
						If ($Script:aDUser.LockedOut) {
							$checkBoxUnlockAcct.Enabled = $true
							$checkBoxUnlockAcct.Checked = $true
						}
						If ((-not $Script:aDUser.CanNotChangePassword) -and (-not $Script:aDUser.PasswordNeverExpires)) {
							$checkBoxChangePwd.Enabled = $true
							$checkBoxChangePwd.Checked = $true
						}
						Write-Log -Message "$($action) user '$($Script:aDUser.sAMAccountName)' ('$($Script:aDUser.DisplayName)'); flags: $($textBoxUserFlags.Text.ToLower())."
						[void]$buttonNewPwd.Focus()
					} Else {
						Write-Log -Message "SamAccountName '$($SamAccountName)' not found!"
					}
				} Catch {
					Write-Log -Message $_.Exception.Message -Type Error
					[System.Windows.Forms.Messagebox]::Show($_.Exception.Message, $Script:myName, 'OK', 'Error')
				}
			}
			
			'buttonNewPwd' {
				$textBoxPwd.Text = New-RandomPassword -Length $Script:passwordLength
			}
			
			'checkBoxShowPwd' {
				$textBoxPwd.PasswordChar = If ($this.Checked) {$null} Else {'*'}
			}
			
			{'buttonApply', 'buttonOK' -contains $_} {
				$exceptions = ''
				$summary = "[$($Script:aDUser.SamAccountName)] Password: "
				$invokeCustomAction = $false
				If (-not [String]::IsNullOrEmpty($textBoxPwd.Text.Trim())) {
					Write-Log -Message 'Setting password ...'
					Try {
						Set-ADAccountPassword -Identity $Script:aDUser.DistinguishedName -NewPassword (ConvertTo-SecureString $textBoxPwd.Text -AsPlaintext -Force) -Reset -Confirm:$false -ErrorAction Stop
						$invokeCustomAction = $true
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAILED'
						$exceptions += "$($_.Exception.Message)`r`n"
					}
				} Else {
					$summary += 'SKIPPED'
				}
				$summary += '; Unlock: '
				If ($checkBoxUnlockAcct.Checked) {
					Write-Log -Message 'Unlocking account ...'
					Try {
						Unlock-ADAccount -Identity $Script:aDUser.DistinguishedName -Confirm:$false -ErrorAction Stop
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAILED'
						$exceptions += "$($_.Exception.Message)`r`n"
					}
				} Else {
					$summary += 'SKIPPED'
				}
				$summary += '; Change: '
				If ($checkBoxChangePwd.Checked) {
					Write-Log -Message 'Setting "Must change password" ...'
					Try {
						Set-ADUser -Identity $Script:aDUser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false -ErrorAction Stop
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAILED'
						$exceptions += "$($_.Exception.Message)"
					}
				} Else {
					$summary += 'SKIPPED'
				}
				If ($exceptions) {
					[System.Windows.Forms.Messagebox]::Show($exceptions, $Script:myName, 'OK', 'Error')
				}
				If ($invokeCustomAction) {
					Invoke-CustomAction -ADUser $Script:aDUser -NewPassword $textBoxPwd.Text
				}
				Write-Log -Message $summary -Type $(If ($exceptions) {'Error'} Else {'Information'})
				Initialize-ControlState
				If ($_ -eq 'buttonOK') {
					$formMain.Close()
				}
			}
			
			'buttonCancel'{
				$formMain.Close()
			}
			
		}
	}
	
	$evt_KeyUp = {
	Param($Sender, $EventArgs)
		Switch ($this.Name) {
			'textBoxSam' {
				$buttonFind.Enabled = -not [String]::IsNullOrEmpty($textBoxSam.Text.Trim())
				If (($EventArgs.KeyCode -eq [System.Windows.Forms.Keys]::Return) -and $buttonFind.Enabled) {
					$buttonFind.PerformClick()
				}
			}
		}
	}
	# endregion events
	
	Function New-Control {
	Param(
		[string]$Type,
		[PSObject]$Parent = $formMain.Controls,
		[bool]$Hide,
		[Parameter(ValueFromRemainingArguments=$True)]
		[PSObject[]]$Props
	)
		$control = New-Object -TypeName System.Windows.Forms.$Type
		$null = $MyInvocation.Line -match '\A\s*\$(?<Variable>.+?)\s*=.*\Z'
		$control.Name =	$Matches['Variable']
		ForEach ($prop In $Props) {
			$prop = $prop.TrimStart('-')
			[void]$ForEach.MoveNext()
			If (($control.$prop -is 'System.Drawing.Point') -or ($control.$prop -is 'System.Drawing.Size')) {
				$control.$prop = New-Object -TypeName ($control.$prop).GetType().FullName -ArgumentList $ForEach.Current
			} ElseIf ($ForEach.Current -is [ScriptBlock]) {
				$control.$prop.Invoke($ForEach.Current)
			} Else {
				$control.$prop = $ForEach.Current
			}
		}
		If ($Parent.Add) {[void]$Parent.Add($control)}
		If ($Hide) {$control.Hide()}
		Return $control
	}
	
	$formMain =				New-Control -Type Form	  -ClientSize 400, 280	-FormBorderStyle FixedSingle -MaximizeBox $false -MinimizeBox $true -Text 'Reset User Password'
	$labelMessage =			New-Control -Type Label		-Location   8,   8	-AutoSize $true	-Text "Please enter the user's SamAccountName (wildcard * accepted):"
	$labelUserSam =			New-Control -Type Label		-Location   8,  32	-AutoSize $true	-Text 'SamAccountName:'
	$textBoxSam =			New-Control -Type TextBox	-Location 120,  32	-Size 192,  20	-add_KeyUp $evt_KeyUp
	$buttonFind =			New-Control -Type Button	-Location 320,  32	-Size  72,  20	-Text '&Find' -add_Click $evt_Click
	$labelUserDisplay =		New-Control -Type Label		-Location   8,  64	-AutoSize $true	-Text 'DisplayName:'
	$textBoxUserDisplay =	New-Control -Type TextBox	-Location 120,  64	-Size 272,  20 	-ReadOnly $true
	$labelUserDN =			New-Control -Type Label		-Location   8,  96	-AutoSize $true	-Text 'DistinguishedName:'
	$textBoxUserDN =		New-Control -Type TextBox	-Location 120,  96	-Size 272,  20 	-ReadOnly $true
	$labelUserFlags =		New-Control -Type Label		-Location   8, 128	-AutoSize $true	-Text 'Account Flags:'
	$textBoxUserFlags =		New-Control -Type TextBox	-Location 120, 128	-Size 272,  20 	-ReadOnly $true
	$buttonNewPwd =			New-Control -Type Button	-Location   8, 160	-Size  96,  20	-Text '&New Password:' -add_Click $evt_Click
	$textBoxPwd =			New-Control -Type TextBox	-Location 120, 160	-Size 192,  20	-PasswordChar '*' -ReadOnly (-not $enableEditPassword)
	$checkBoxShowPwd =		New-Control -Type CheckBox	-Location 320, 160	-AutoSize $true	-Text '&Show' -add_Click $evt_Click -Hide (-not $enableShowPassword)
	$checkBoxUnlockAcct =	New-Control -Type CheckBox	-Location   8, 192	-AutoSize $true	-Text '&Unlock account'
	$checkBoxChangePwd =	New-Control -Type CheckBox	-Location 120, 192	-AutoSize $true	-Text 'User &must change password at next logon'
	$buttonOK =				New-Control -Type Button	-Location 160, 224	-Size  72,  25	-Text '&OK'		-add_Click $evt_Click
	$buttonCancel =			New-Control -Type Button	-Location 240, 224	-Size  72,  25	-Text '&Cancel'	-add_Click $evt_Click
	$buttonApply =			New-Control -Type Button	-Location 320, 224	-Size  72,  25	-Text '&Apply'	-add_Click $evt_Click
	$statusBar =			New-Control -Type StatusBar	-ShowPanels $true -SizingGrip $false
	$statusBarPanelLog =	New-Control -Type StatusBarPanel	-Parent $statusBar.Panels	-BorderStyle Sunken	-AutoSize Spring

	$formMain.CancelButton =	$buttonCancel
	$formMain.AcceptButton =	$buttonApply

	Initialize-ControlState
	[void]$formMain.ShowDialog()
}

Show-FormMain

Open in new window

Avatar of cawasaki
cawasaki

ASKER

hello,

thank you :)

i will test it, where i can insert my code to send password to use on printer queue?

$PrintServer = @{
	'site1' = '\\server1\print'
	'site2' = '\\server2\print'
	'site3' = '\\server3\print'
	}

and

$SSAPassword1 | out-file -Encoding Ascii -filepath D:\script\resetpassword\password.txt
							$SSAUserOU = $QueryADUser.DistinguishedName -replace '\ACN=(?:.+?),OU=(.+?),(OU|DC)=.*', '$1'
							$PrinterQueue = $PrintServer[$SSAUserOU]
                                                        PsExec.exe -u domain\$SSAUserName -p $SSAPassword1 cmd.exe /c "PRINT /d:$PrinterQueue D:\script\projetresetpassword\password.txt"
                                                        remove-item -path D:\script\projetresetpassword\*.txt

Open in new window

Avatar of oBdA
oBdA

As I said: the function Invoke-CustomAction (stub in lines 9-12) is all yours.
Untested:
Function Invoke-CustomAction {
Param($ADUser, $NewPassword)
	$printServer = @{
		'site1' = '\\server1\print'
		'site2' = '\\server2\print'
		'site3' = '\\server3\print'
	}
	$passwordFile = "D:\script\projetresetpassword\password_$($ADUser.SamAccountName).txt"
	$NewPassword | Out-File -Encoding Ascii -FilePath $passwordFile
	$sSAUserOU = $ADUser.DistinguishedName -replace '\ACN=(?:.+?),OU=(.+?),(OU|DC)=.*', '$1'
	$printerQueue = $printServer[$sSAUserOU]
	Write-Log -Message "Printing password to $($printerQueue) ..."
	& PsExec.exe -u domain\$($ADUser.SamAccountName) -p "`"$($NewPassword)`"" cmd.exe /c "PRINT /d:$($printerQueue) `"$($passwordFile)`""
	Remove-Item -Path $passwordFile
	Write-Log -Message "Password printed to $($printerQueue)"
}

Open in new window

Avatar of cawasaki
cawasaki

ASKER

hello,

i have error on Psexec part:

PS D:\resetpassword\> .\reset_password.ps1
[2018-05-18 10:06:41] [Information] Loading Active Directory Module ...
[2018-05-18 10:06:43] [Information] Searching 'toto*' ...
[2018-05-18 10:06:43] [Information] Found user 'toto.titi' ('TOTO Titi'); flags: passwd_must_change.
[2018-05-18 10:06:47] [Information] Setting password ...
[2018-05-18 10:06:47] [Information] Setting "Must change password" ...
[2018-05-18 10:06:47] [Information] Printing password to \\printserver\printqueue1 ...

PsExec v2.11 - Execute processes remotely
Copyright (C) 2001-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

PsExec could not start cmd.exe:
The user name or password is incorrect.
[2018-05-18 10:07:01] [Information] Running custom action ...
[2018-05-18 10:07:01] [Information] [toto.titi] Password: OK; Unlock: SKIPPED; Change: OK

Open in new window

Avatar of oBdA
oBdA

Did you adjust the domain name in the psexec call?
Avatar of cawasaki
cawasaki

ASKER

hello,

oups i missed this.

now i have other error:

PsExec v2.11 - Execute processes remotely
Copyright (C) 2001-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

PsExec could not start cmd.exe:
The user's password must be changed before signing in.
[2018-05-18 13:34:20] [Information] Running custom action ...
[2018-05-18 13:34:20] [Information] [toto.titi] Password: OK; Unlock: SKIPPED; Change: OK

Open in new window

Avatar of oBdA
oBdA

For the time being, uncheck the "User must change password" for the test user using the ADUC, and the next time you run the password GUI, uncheck the "User must change password" before resetting it the next time.
Then let me know whether you need the "User must change password" in general.
Won't be able to adjust this before sunday or monday, though.
Avatar of cawasaki
cawasaki

ASKER

hello,

i confirm that uncheck the "User must change password" work,

no need to be an option on the gui, evry time we change password we need the user to change password, so may be use this directly on the code?
this option need to be activated after send password on print queue, in my old script i pass this command at the end:

Set-ADUser -Identity $SSAUserName -ChangePasswordAtLogon $True....

thanks for help
Avatar of cawasaki
cawasaki

ASKER

Hello oBdA,

any news?

thanks
Avatar of oBdA
oBdA

This now makes sure that "must change password" is disabled before calling the custom action, and calls the custom action before setting "must change password"
$Script:enableShowPassword = $true
$Script:enableEditPassword = $true
$Script:passwordLength = 10

$Script:dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
$Script:aDUserProperties = 'SamAccountName', 'DisplayName', 'DistinguishedName', 'Enabled', 'LockedOut', 'CanNotChangePassword', 'PasswordNeverExpires', 'PasswordLastSet'
$Script:myName = $MyInvocation.MyCommand.Name

Function Invoke-CustomAction {
Param($ADUser, $NewPassword)
	$printServer = @{
		'site1' = '\\server1\print'
		'site2' = '\\server2\print'
		'site3' = '\\server3\print'
	}
	$passwordFile = "D:\script\projetresetpassword\password_$($ADUser.SamAccountName).txt"
	
	Write-Log -Message "Printing password ..."
	$NewPassword | Out-File -Encoding Ascii -FilePath $passwordFile
	$sSAUserOU = $ADUser.DistinguishedName -replace '\ACN=(?:.+?),OU=(.+?),(OU|DC)=.*', '$1'
	$printerQueue = $printServer[$sSAUserOU]
	Write-Log -Message "Printing password to $($printerQueue) ..."
	& PsExec.exe -u "$($ENV:UserDomain)\$($ADUser.SamAccountName)" -p "`"$($NewPassword)`"" cmd.exe /c "PRINT /d:$($printerQueue) `"$($passwordFile)`""
	Remove-Item -Path $passwordFile
	Write-Log -Message "Password printed to $($printerQueue)"
}

Function New-RandomPassword {
Param([ValidateRange(9, 32)][Int32]$Length = 9)
	$categories = @{
		'Lower' =		'abcdefghijkmnopqrstuvwxyz'	# Lowercase l might be confused with digit 1 or uppercase i
		'Upper' =		'ABCDEFGHJKLMNPQRSTUVWXYZ'	# Uppercase i might be confused with digit 1 or lower-case L; uppercase o might be confused with digit 0
		'Digits' =		'23456789'					# 0 might be confused with uppercase o; 1 might be confused with lowercase L or uppercase i.
		'NonAlpha' =	'!#$%&()*+,-./:;<=>?@[\]_{}~'
	}
	$characterPool = $password = ''
	ForEach ($category In 'Lower', 'Upper', 'Digits') {
		1..3 | ForEach-Object {$password += $categories[$category][(Get-Random -Maximum $categories[$category].Length)]}
		$characterPool += $categories[$category]
	}
	For ($i = $password.Length; $i -lt $Length; $i++) {
		$password += $characterPool[(Get-Random -Maximum $characterPool.Length)]
	}
	Return (-join [char[]]([byte[]][char[]]$password | Get-Random -Count ([Int32]::MaxValue)))
}

Function Show-FormMain {
	Add-Type -AssemblyName System.Drawing
	Add-Type -AssemblyName System.Windows.Forms
	[System.Windows.Forms.Application]::EnableVisualStyles()

	Function Write-Log {
	Param([String]$Message, [ValidateSet('Information', 'Warning', 'Error')]$Type = 'Information')
		$ForegroundColor = Switch ($Type) {'Information' {'White'}; 'Warning' {'Yellow'}; 'Error' {'Red'}}
		Write-Host "[$(Get-Date -Format $Script:dateTimeFormat)] [$($Type)] $($Message)" -ForegroundColor $ForegroundColor
		$statusBarPanelLog.Text = $Message
	}
	
	Function Initialize-ControlState {
		$buttonFind, $buttonNewPwd, $textBoxPwd, $checkBoxShowPwd, $checkBoxUnlockAcct, $checkBoxChangePwd, $buttonOK, $buttonApply | ForEach-Object {$_.Enabled = $false}
		$textBoxSam, $textBoxUserDisplay, $textBoxUserDN, $textBoxUserFlags, $textBoxPwd | ForEach-Object {$_.Text = ''}
		$textBoxPwd.PasswordChar = '*'
		$textBoxSam.ReadOnly = $false
		$buttonFind.Text = '&Find'
		$checkBoxShowPwd, $checkBoxUnlockAcct, $checkBoxChangePwd | ForEach-Object {$_.Checked = $false}
		[void]$textBoxSam.Focus()
	}

	# region events
	$evt_Click = {
	Param($Sender, $EventArgs)
		Switch ($this.Name) {
		
			'buttonFind' {
				If ($textBoxSam.ReadOnly) {		# buttonFind has the 'Clear' function
					Initialize-ControlState
					Return
				}
				If ([string]::IsNullOrEmpty($textBoxSam.Text.Trim())) {
					Return
				}
				$samAccountName = $textBoxSam.Text
				Try {
					If (-not (Get-Module -Name ActiveDirectory)) {
						Write-Log -Message 'Loading Active Directory Module ...'
						Import-Module ActiveDirectory -ErrorAction Stop -Function Get-ADUser, Set-ADAccountPassword, Set-ADUser, Unlock-ADAccount
					}
					Write-Log -Message "Searching '$($samAccountName)' ..."
					If ($Script:aDUser = @(Get-ADUser -Filter {sAMAccountName -like $samAccountName} -Property $Script:aDUserProperties -ErrorAction Stop | Select-Object -Property $Script:aDUserProperties)) {
						If ($Script:aDUser.Count -eq 1) {
							$action = 'Found'
						} Else {
							Write-Log -Message "Found $($Script:aDUser.Count) users matching '$($samAccountName)', waiting for selection ..."
							If (-not ($Script:aDUser = $Script:aDUser | Out-GridView -Title "Multiple matches; please select" -OutputMode Single)) {
								Write-Log -Message "Operation canceled!"
								Return
							}
							$action = 'Selected'
						}
						$textBoxSam.ReadOnly = $true
						$textBoxSam.Text = $Script:aDUser.sAMAccountName
						$textBoxUserDisplay.Text = $Script:aDUser.DisplayName
						$textBoxUserDN.Text = $Script:aDUser.distinguishedName
						$accountFlags = @()
						If (-not $aDUser.Enabled)			{$accountFlags += 'DISABLED'}
						If ($aDUser.LockedOut)				{$accountFlags += 'LOCKEDOUT'}
						If ($aDUser.CanNotChangePassword)	{$accountFlags += 'PASSWD_CANT_CHANGE'}
						If ($aDUser.PasswordNeverExpires)	{$accountFlags += 'DONT_EXPIRE_PASSWORD'}
						If (-not $aDUser.PasswordLastSet)	{$accountFlags += 'PASSWD_MUST_CHANGE'}
						$textBoxUserFlags.Text = $accountFlags -join ', '
						$buttonFind.Text = 'C&lear'
						$buttonNewPwd, $textBoxPwd, $checkBoxShowPwd, $buttonOK, $buttonApply | ForEach-Object {$_.Enabled = $true}
						If ($Script:aDUser.LockedOut) {
							$checkBoxUnlockAcct.Enabled = $true
							$checkBoxUnlockAcct.Checked = $true
						}
						If ((-not $Script:aDUser.CanNotChangePassword) -and (-not $Script:aDUser.PasswordNeverExpires)) {
							$checkBoxChangePwd.Enabled = $true
							$checkBoxChangePwd.Checked = $true
						}
						Write-Log -Message "$($action) user '$($Script:aDUser.sAMAccountName)' ('$($Script:aDUser.DisplayName)'); flags: $($textBoxUserFlags.Text.ToLower())."
						[void]$buttonNewPwd.Focus()
					} Else {
						Write-Log -Message "SamAccountName '$($SamAccountName)' not found!"
					}
				} Catch {
					Write-Log -Message $_.Exception.Message -Type Error
					[System.Windows.Forms.Messagebox]::Show($_.Exception.Message, $Script:myName, 'OK', 'Error')
				}
			}
			
			'buttonNewPwd' {
				$textBoxPwd.Text = New-RandomPassword -Length $Script:passwordLength
			}
			
			'checkBoxShowPwd' {
				$textBoxPwd.PasswordChar = If ($this.Checked) {$null} Else {'*'}
			}
			
			{'buttonApply', 'buttonOK' -contains $_} {
				$exceptions = ''
				$summary = "[$($Script:aDUser.SamAccountName)] PWD: "
				$invokeCustomAction = $false
				If (-not [String]::IsNullOrEmpty($textBoxPwd.Text.Trim())) {
					Write-Log -Message 'Setting password ...'
					Try {
						Set-ADAccountPassword -Identity $Script:aDUser.DistinguishedName -NewPassword (ConvertTo-SecureString $textBoxPwd.Text -AsPlaintext -Force) -Reset -Confirm:$false -ErrorAction Stop
						$invokeCustomAction = $true
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAIL'
						$exceptions += "$($_.Exception.Message)`r`n"
					}
				} Else {
					$summary += 'skip'
				}
				$summary += '; UNLCK: '
				If ($checkBoxUnlockAcct.Checked) {
					Write-Log -Message 'Unlocking account ...'
					Try {
						Unlock-ADAccount -Identity $Script:aDUser.DistinguishedName -Confirm:$false -ErrorAction Stop
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAIL'
						$exceptions += "$($_.Exception.Message)`r`n"
					}
				} Else {
					$summary += 'skip'
				}
				$summary += '; CSTM: '
				If ($invokeCustomAction) {
					Write-Log -Message 'Running custom action ...'
					Try {
						If (-not $aDUser.PasswordLastSet) {
							Write-Log -Message "Disabling 'Must change password' ..."
							Set-ADUser -Identity $Script:aDUser.DistinguishedName -ChangePasswordAtLogon $false -Confirm:$false -ErrorAction Stop
						}
						Invoke-CustomAction -ADUser $Script:aDUser -NewPassword $textBoxPwd.Text -ErrorAction Stop
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAIL'
						$exceptions += "$($_.Exception.Message)`r`n"
					}
				} Else {
					$summary += 'skip'
				}
				$summary += '; CHGPWD: '
				If ($checkBoxChangePwd.Checked) {
					Write-Log -Message 'Setting "Must change password" ...'
					Try {
						Set-ADUser -Identity $Script:aDUser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false -ErrorAction Stop
						$summary += 'OK'
					} Catch {
						Write-Log -Message $_.Exception.Message -Type Error
						$summary += 'FAIL'
						$exceptions += "$($_.Exception.Message)"
					}
				} Else {
					$summary += 'skip'
				}
				If ($exceptions) {
					[System.Windows.Forms.Messagebox]::Show($exceptions, $Script:myName, 'OK', 'Error')
				}
				Write-Log -Message $summary -Type $(If ($exceptions) {'Error'} Else {'Information'})
				Initialize-ControlState
				If ($_ -eq 'buttonOK') {
					$formMain.Close()
				}
			}
			
			'buttonCancel'{
				$formMain.Close()
			}
			
		}
	}
	
	$evt_KeyUp = {
	Param($Sender, $EventArgs)
		Switch ($this.Name) {
			'textBoxSam' {
				$buttonFind.Enabled = -not [String]::IsNullOrEmpty($textBoxSam.Text.Trim())
				If (($EventArgs.KeyCode -eq [System.Windows.Forms.Keys]::Return) -and $buttonFind.Enabled) {
					$buttonFind.PerformClick()
				}
			}
		}
	}
	# endregion events
	
	Function New-Control {
	Param(
		[string]$Type,
		[PSObject]$Parent = $formMain.Controls,
		[bool]$Hide,
		[Parameter(ValueFromRemainingArguments=$True)]
		[PSObject[]]$Props
	)
		$control = New-Object -TypeName System.Windows.Forms.$Type
		$null = $MyInvocation.Line -match '\A\s*\$(?<Variable>.+?)\s*=.*\Z'
		$control.Name =	$Matches['Variable']
		ForEach ($prop In $Props) {
			$prop = $prop.TrimStart('-')
			[void]$ForEach.MoveNext()
			If (($control.$prop -is 'System.Drawing.Point') -or ($control.$prop -is 'System.Drawing.Size')) {
				$control.$prop = New-Object -TypeName ($control.$prop).GetType().FullName -ArgumentList $ForEach.Current
			} ElseIf ($ForEach.Current -is [ScriptBlock]) {
				$control.$prop.Invoke($ForEach.Current)
			} Else {
				$control.$prop = $ForEach.Current
			}
		}
		If ($Parent.Add) {[void]$Parent.Add($control)}
		If ($Hide) {$control.Hide()}
		Return $control
	}
	
	$formMain =				New-Control -Type Form	  -ClientSize 400, 280	-FormBorderStyle FixedSingle -MaximizeBox $false -MinimizeBox $true -Text 'Reset User Password'
	$labelMessage =			New-Control -Type Label		-Location   8,   8	-AutoSize $true	-Text "Please enter the user's SamAccountName (wildcard * accepted):"
	$labelUserSam =			New-Control -Type Label		-Location   8,  32	-AutoSize $true	-Text 'SamAccountName:'
	$textBoxSam =			New-Control -Type TextBox	-Location 120,  32	-Size 192,  20	-add_KeyUp $evt_KeyUp
	$buttonFind =			New-Control -Type Button	-Location 320,  32	-Size  72,  20	-Text '&Find' -add_Click $evt_Click
	$labelUserDisplay =		New-Control -Type Label		-Location   8,  64	-AutoSize $true	-Text 'DisplayName:'
	$textBoxUserDisplay =	New-Control -Type TextBox	-Location 120,  64	-Size 272,  20 	-ReadOnly $true
	$labelUserDN =			New-Control -Type Label		-Location   8,  96	-AutoSize $true	-Text 'DistinguishedName:'
	$textBoxUserDN =		New-Control -Type TextBox	-Location 120,  96	-Size 272,  20 	-ReadOnly $true
	$labelUserFlags =		New-Control -Type Label		-Location   8, 128	-AutoSize $true	-Text 'Account Flags:'
	$textBoxUserFlags =		New-Control -Type TextBox	-Location 120, 128	-Size 272,  20 	-ReadOnly $true
	$buttonNewPwd =			New-Control -Type Button	-Location   8, 160	-Size  96,  20	-Text '&New Password:' -add_Click $evt_Click
	$textBoxPwd =			New-Control -Type TextBox	-Location 120, 160	-Size 192,  20	-PasswordChar '*' -ReadOnly (-not $enableEditPassword)
	$checkBoxShowPwd =		New-Control -Type CheckBox	-Location 320, 160	-AutoSize $true	-Text '&Show' -add_Click $evt_Click -Hide (-not $enableShowPassword)
	$checkBoxUnlockAcct =	New-Control -Type CheckBox	-Location   8, 192	-AutoSize $true	-Text '&Unlock account'
	$checkBoxChangePwd =	New-Control -Type CheckBox	-Location 120, 192	-AutoSize $true	-Text 'User &must change password at next logon'
	$buttonOK =				New-Control -Type Button	-Location 160, 224	-Size  72,  25	-Text '&OK'		-add_Click $evt_Click
	$buttonCancel =			New-Control -Type Button	-Location 240, 224	-Size  72,  25	-Text '&Cancel'	-add_Click $evt_Click
	$buttonApply =			New-Control -Type Button	-Location 320, 224	-Size  72,  25	-Text '&Apply'	-add_Click $evt_Click
	$statusBar =			New-Control -Type StatusBar	-ShowPanels $true -SizingGrip $false
	$statusBarPanelLog =	New-Control -Type StatusBarPanel	-Parent $statusBar.Panels	-BorderStyle Sunken	-AutoSize Spring

	$formMain.CancelButton =	$buttonCancel
	$formMain.AcceptButton =	$buttonApply

	Initialize-ControlState
	[void]$formMain.ShowDialog()
}

Show-FormMain

Open in new window

Avatar of cawasaki
cawasaki

ASKER

hello,

the first test is a success, i need to do more test.

i have 2 questions:

1- why the gui is closed automaticly after every password changed? its possible to not close?

2- are this can be detected autmaticly so every helpdesk admin can use the gui without change this line ? :  $passwordFile = "D:\script\projetresetpassword\password_$($ADUser.SamAccountName).txt"

thanks for help
Avatar of oBdA
oBdA

1. Click "Apply" instead of "OK". That's standard Windows dialog behavior.
2. That was your code. You can change that to whatever you need/want to. This specific file, though, looks like it belongs in the temp folder.
$passwordFile = "${ENV:Temp}\password_$($ADUser.SamAccountName).txt".
Avatar of cawasaki
cawasaki

ASKER

ok when i press apply, the gui still opened .

for the second question, what i mean is every admin will copy the script in specific folder, and will do a write click on ps1 file and click on run with powershell, it possible that the script detect automaticly from what folder the script is run?

thanks
Avatar of oBdA
oBdA

Yes, but the file to print shouldn't end in the script's folder. This is a classic case for a temp file.
So in line 16, use
	$passwordFile = "${ENV:Temp}\password_$($ADUser.SamAccountName).txt"

Open in new window

Avatar of cawasaki
cawasaki

ASKER

where will be the file to print? this is no problem because it will be deleted. and i tested and it work.

other questions:

- what version of powershell need to be present on computer for this script to work?
-it possible to execute the script with other user and password? for exemple i am connected to my computer with normal user, and i need to execute the script with my admin account.

thanks for help
ASKER CERTIFIED SOLUTION
Avatar of oBdA
oBdA

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
Avatar of cawasaki
cawasaki

ASKER

thank you very much oBdA.

sorry but my last demand: its possible when the prompt to use different user i have an option to use the actual user? this to avoid when the admin is already conneted with admin user.

thank you
Avatar of oBdA
oBdA

Just press Escape or Cancel on the credentials dialog.
Note that the actual printing part is currently still commented out (lines 27 and 28).
Avatar of cawasaki
cawasaki

ASKER

thank you for help 100000 points for you :)
Avatar of cawasaki
cawasaki

ASKER

hello oBdA,

i have found a problem,
this line is not good, the file is not came to end user:

& PsExec.exe -u "$($ENV:UserDomain)\$($ADUser.SamAccountName)" -p "`"$($NewPassword)`"" cmd.exe /c "PRINT /d:$($printerQueue) `"$($passwordFile)`""

Open in new window

Avatar of cawasaki
cawasaki

ASKER

i found the problem, this line:
with this line its work
$passwordFile = "D:\script\projetresetpassword\password_$($ADUser.SamAccountName).txt"

but when you have modify to this its not work:

$passwordFile = "${ENV:Temp}\password_$($ADUser.SamAccountName).txt"
Avatar of oBdA
oBdA

Sorry, can't reproduce.
In cmd.exe /c ..., replace the "/c" with "/k" so you can see any errors.
Avatar of cawasaki
cawasaki

ASKER

hello,

here the error:

Can't find file C:\Users\admin\AppData\Local\Temp\password_toto.txt

Open in new window


in C:\Users\admin\AppData\Local\Temp, can see that password_toto.txt is here.
Avatar of oBdA
oBdA

Silly me. The print job is running as a different (non-admin) user and will have no access to the helpdesk user's profile folder.
You need to put that into a folder where regular users definitely have Read access.
Avatar of cawasaki
cawasaki

ASKER

hello oBdA,

when i have test the script, i have test it in powershell v4 and its work.
i have test today on powershell v5.1 and i have error with psexec:

[2018-06-06 11:03:47] [Error] The term 'PsExec.exe' is not recognized as the name of a cmdlet, function, script file, or
 operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try ag
ain.
[2018-06-06 11:03:56] [Error] [username] PWD: OK; UNLCK: skip; CSTM: FAIL; CHGPWD: skip

Suggestion [3,General]: The command PsExec.exe was not found, but does exist in the current location. Windows PowerShell
 does not load commands from the current location by default. If you trust this command, instead type: ".\PsExec.exe". S
ee "get-help about_Command_Precedence" for more details.

Open in new window


thanks for help
Avatar of oBdA
oBdA

The "Suggestion" at the end of the error message contains the solution.
Avatar of cawasaki
cawasaki

ASKER

i found the problem, i have copy psexec in c:\windows\stsem32 and it ok :)
Scripting Languages
Scripting Languages

A scripting language is a programming language that supports scripts, programs written for a special run-time environment that automate the execution of tasks that could alternatively be executed one-by-one by a human operator. Scripting languages are often interpreted (rather than compiled). Primitives are usually the elementary tasks or API calls, and the language allows them to be combined into more complex programs. Environments that can be automated through scripting include software applications, web pages within a web browser, the shells of operating systems (OS), embedded systems, as well as numerous games. A scripting language can be viewed as a domain-specific language for a particular environment; in the case of scripting an application, this is also known as an extension language.

30K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo