Share via


Passing variables from with a form function

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