Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Wednesday, June 4, 2014 4:46 PM
I am trying to create a powershell script that will complete an imaging process that we use for hundreds of standardized computers. I have a form that opens and allows for input for a few minor changes to the computer. This is in a function and all of the process are also functions to keep them organized. Problem I am having is that the variables set in the form function are not able to be used outside of the function. I assume that this has to do with global variables. Some of the form was from another site but i altered for my use. Can anyone help me with this please. Below is a snip of the script - the input form and the renaming of a user account. If I can fix this part I can figure out why the rest wont work.
In the form I ask for input on what you want an account renamed to that is on the image. When I run this though - the variable is not passed to second function.
Couple other things I cant figure out - if you hit cancel on the input - how do you terminate the script completely
If you hit "Enter" instead of clicking "OK" it doesnt keep the variables even within the function
Thanks
#Add needed Assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
#Create Cleanup Variable
$startupVariables=""
new-variable -force -name startupVariables -value ( Get-Variable |
% { $_.Name } )
#Cleanup Variables
function Cleanup-Variables {
Get-Variable |
Where-Object { $startupVariables -notcontains $_.Name } |
% { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" }
}
#Gather User Input
function Gather-Input {
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "New Build Installation"
$objForm.Size = New-Object System.Drawing.Size(500,300)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objTextBox.Text;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(10,220)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({
$newUser=$objTextBox.Text
$Number=$objTextBox2.Text
$DomainUser=$objTextBox3.Text
$DomainPass=$objTextBox4.Text
$objForm.Close()})
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(90,220)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "What would you like the user account renamed to:"
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,70)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "What is the #:"
$objForm.Controls.Add($objLabel)
$objTextBox2 = New-Object System.Windows.Forms.TextBox
$objTextBox2.Location = New-Object System.Drawing.Size(10,90)
$objTextBox2.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox2)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,120)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "Enter a username that can be used to join the domain:"
$objForm.Controls.Add($objLabel)
$objTextBox3 = New-Object System.Windows.Forms.TextBox
$objTextBox3.Location = New-Object System.Drawing.Size(10,140)
$objTextBox3.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox3)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,170)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "Enter the password for the above account:"
$objForm.Controls.Add($objLabel)
$objTextBox4 = New-Object System.Windows.Forms.MaskedTextBox
$objTextBox4.PasswordChar = '*'
$objTextBox4.Location = New-Object System.Drawing.Size(10,190)
$objTextBox4.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox4)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
}
#Rename User Account / Delete Setup User
function Rename-UserAccount {
$User = [ADSI](“WinNT://$Env:COMPUTERNAME/mobileuser,User”)
$User.psbase.rename($newUser)
$delUser = "setup"
$computer = [adsi]("WinNT://$env:COMPUTERNAME")
$computer.psbase.invoke("Delete","User",$delUser)
}
Gather-Input
Write-Progress -Activity "Installation" -Status 'Renaming User Account and Deleting Setup Account' -percentcomplete 5
start-sleep -seconds 5
Rename-UserAccount
All replies (5)
Wednesday, June 4, 2014 5:06 PM ✅Answered | 1 vote
As a general rule, functions should accept all input via parameters, and all output should go through the output stream (or, on occasion, via a [ref] parameter). Also, the Windows Forms examples that you probably used as a basis for your Gather-Input function don't work properly in PowerShell 3.0 and later; something about variable scopes changed, and the examples were never updated. Instead of trying to write values to variables in a Click event, I find that it's cleaner to read the values out of the text boxes after calling ShowDialog() (assuming the user pressed enter or clicked OK).
Here's a revision of your code which takes these ideas into account. I got rid of the Click and KeyDown events on the buttons and form; instead, I assigned values to the buttons' DialogResult properties, and set the AcceptButton / CancelButton properties of the form (which makes it handle the Enter and Escape key sequences.) I modified your Gather-Input function to output a custom object with the 4 properties that you were trying to assign to global variables before, and modified Rename-UserAccount to accept pipeline input (by property name) for $NewUser, so the object that came from Gather-Input can be piped in.
Instead of calling Rename-UserAccount this way, with pipeline input, you could also make it explicit like this: Rename-UserAccount -NewUser $userInput.NewUser . That's personal preference.
#Add needed Assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
function Gather-Input
{
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "New Build Installation"
$objForm.Size = New-Object System.Drawing.Size(500,300)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(10,220)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$objForm.AcceptButton = $OKButton
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(90,220)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$objForm.CancelButton = $CancelButton
$objForm.Controls.Add($CancelButton)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "What would you like the user account renamed to:"
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,70)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "What is the #:"
$objForm.Controls.Add($objLabel)
$objTextBox2 = New-Object System.Windows.Forms.TextBox
$objTextBox2.Location = New-Object System.Drawing.Size(10,90)
$objTextBox2.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox2)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,120)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "Enter a username that can be used to join the domain:"
$objForm.Controls.Add($objLabel)
$objTextBox3 = New-Object System.Windows.Forms.TextBox
$objTextBox3.Location = New-Object System.Drawing.Size(10,140)
$objTextBox3.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox3)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,170)
$objLabel.Size = New-Object System.Drawing.Size(400,20)
$objLabel.Text = "Enter the password for the above account:"
$objForm.Controls.Add($objLabel)
$objTextBox4 = New-Object System.Windows.Forms.MaskedTextBox
$objTextBox4.PasswordChar = '*'
$objTextBox4.Location = New-Object System.Drawing.Size(10,190)
$objTextBox4.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox4)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
$dialogResult = $objForm.ShowDialog()
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK)
{
New-Object psobject -Property @{
NewUser = $objTextBox.Text
Number = $objTextBox2.Text
DomainUser = $objTextBox3.Text
DomainPass = $objTextBox4.Text
}
}
else
{
throw 'Operation cancelled by user.'
}
}
function Rename-UserAccount
{
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]
$NewUser
)
process
{
$User = [ADSI](“WinNT://$Env:COMPUTERNAME/mobileuser,User”)
$User.psbase.rename($NewUser)
$delUser = "setup"
$computer = [adsi]("WinNT://$env:COMPUTERNAME")
$computer.psbase.invoke("Delete","User",$delUser)
}
}
$userInput = Gather-Input
Write-Progress -Activity "Installation" -Status 'Renaming User Account and Deleting Setup Account' -percentcomplete 5
start-sleep -seconds 5
$userInput | Rename-UserAccount
Wednesday, June 4, 2014 8:16 PM ✅Answered | 1 vote
This line looks a bit fishy to me:
$credential = New-Object System.Management.Automation.PSCredential $($domain+"\"+$DomainUser,$SecurePass)
The comma operator has a higher precedence than +, if I remember correctly. If so, you can fix that by putting parentheses around the expression to create the username, or just use the $FQDomainUser variable that you created earlier with the same value.
Also, there's no need to use a subexpression operator there $(), or in any of the other places you've used it in this function. You mostly only require that when you're embedding an expression inside a double-quoted string. Try this:
process
{
$domain = "mydomain"
$FQDomainUser = $domain + "\" + $DomainUser
$SecurePass = ConvertTo-SecureString $DomainPass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($FQDomainUser, $SecurePass)
Add-Computer -DomainName $domain -Credential $credential -OUPath 'OU=Windows7Computers,DC=mydomain,DC=org'
start-sleep -seconds 15
}
Wednesday, June 4, 2014 5:50 PM
This worked - thank you for your guidance and help!
Wednesday, June 4, 2014 8:09 PM
Well I spoke to soon... Your example has helped me out with all except one function - joining the domain.
Can you provide any guidance or better way to do this? I followed your example but I am not sure what I am doing wrong. I called the last two input variables into the function not sure if I did it correctly but I did verify they both made it into function. Have to convert password to secure string - the rest doesnt seem to be working as it pops up the cred window instead of accepting my variables.
Here is the join domain function modeled after your above example.
#Add computer to the domainfunction JoinDomain{ [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$DomainUser, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$DomainPass ) process { #$DomainPass #$DomainUser $FQdomain = "mydomain.org" $domain = "mydomain" $FQDomainUser = $domain + "\" + $DomainUser $SecurePass = ConvertTo-SecureString $($DomainPass) -AsPlainText -Force #$SecurePass $credential = New-Object System.Management.Automation.PSCredential $($domain+"\"+$DomainUser,$SecurePass) Add-Computer -DomainName $domain -Credential $credential -OUPath 'OU=Windows7Computers,DC=mydomain,DC=org' start-sleep -seconds 15 }}
Wednesday, June 4, 2014 8:14 PM
Disregard - I just fixed it...
Change $credential = New-Object System.Management.Automation.PSCredential $($domain+"\+$DomainUser,$SecurePass)
$credential = New-Object System.Management.Automation.PSCredential $($domain+"\"+$DomainUser,$SecurePass)
$credential = New-Object System.Management.Automation.PSCredential $($FQDomainUser,$SecurePass)
It is working now but again - if you have suggestions on how I could have made this portion of the script better I would appreciate the guidance.
Thanks