How to save different file paths to multiple variables in powershell - winforms

I have a powershell form with a Browse button that is used to pick out a file and then the path to that file is saved into a textbox variable and displayed in the form. That part works fine, however, I have 3 text boxes that I'd like have filled in independently of each other, each with their own file path if the user decides to pick 3 different files. Make sense?
Here's the code...
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
[void]$OpenFileDialog.ShowDialog()
If ($networktext1.Text -eq "") { $networktext1.Text = $OpenFileDialog.FileName }
If ($networktext2.Text -eq "") { $networktext2.Text = $OpenFileDialog.FileName }
If ($networktext3.Text -eq "") { $networktext3.Text = $OpenFileDialog.FileName }
If (($networktext1.Text -ne "") -and ($networktext2.Text -ne "") -and ($networktext3.Text -ne "")) { $logbox.Items.Add(" ERROR: No network slots available") }
I've played around with it every way I can think of and I can't get it to only fill in the first box, or if that one is taken only fill in the second box and so on. Obviously the code above will fill in all 3 boxes the first time a file is chosen and then give the error that they're all full. I brought it back to that point so it'd be easier to follow and modify with possible solutions.
Thanks!

Related

Powershell - Exporting data from powershell into a csv file using custom objects

So I received a list of users from a co-worker who needed to confirm who in the list was still employed and who wasn't. I chose to filter out all users that either didn't exist in AD or were disabled and assign them to $TerminatedUser. I took all active users that assigned them to $EmployeedUser. (I know I spelled "Employed" wrong) I then tried to use the data from $EmployeedUser and $TerminatedUser and create a report within $EmployementStatus.
What I end up with is two columns which is awesome but I also only get 1 cell for each column. All the data for each column is bunched into one cell which makes it hard to read. At first when outputting $EmployementStatus to a csv file was only getting the headers and [system.object] for each cell. I was able to get around that.
So my question here now is: Is it possible to export $EmployementStatus to a csv where the data is listed out and each "Employed"/"Terminated" user receives their own cell as opposed to them all being bunched in cells A2 and B2?
Teach me something!
This is sample code, since I'm not going to type out all that stuff again. And it isn't tested.
What you want, apparently, is to check there's an enabled AD user account that matches your userlist. For Powershell versions greater than 3.0, you can output [pscustomobject] directly into an array from a Foreach.
You just need ONE query to AD to determine if a user exists and whether the account is enabled ("Enabled" is one of the default properties returned in Get-AdUser).
It's probably more convenient for output if you simply have a "Verified" column and set that to TRUE or FALSE. Or you can have a "Status" column and output text to that like "Disabled" or "NotPresent" or "Verified". Whatever, really, I'm going with the easiest.
The try/catch is so you don't get a load of errors when the user doesn't exist. If you want to set different statuses for each "state", then you can place strings in there rather than $true/$false.
$employmentStatus = Foreach ($GID in $MyList) {
$ID = $GID.SamAccountname
try {
# if the user isn't found, it'll go to the Catch block after the next line
$u = get-aduser $ID -erroraction stop
if ($u.enabled) {
$verified = $true
}
else {
$verified = $false
}
}
catch {
# if the user doesn't exist, they're not verified
$verified = $false
}
# output the per-user status as a pscustomobject in $employmentStatus
[pscustomobject]#{
ADUser = $ID
Verified = $verified
}
}
You should find that if you process your userlist with that, you can check the result with $employmentStatus | out-gridview.
That should show the "AdUser" and "Verified" columns, with TRUE or FALSE for each user.
If that looks OK, so will your CSV export: $employmentStatus | export-csv [path].
If you're using an old PS version, then you may need to predefine your output array as you did originally. Then you'd just fix up the line with the [pscustomobject] to append it to the array. Everything else works the same.
$employmentStatus = #()
Foreach ($GID in $MyList) {
...
# output the per-user status as a pscustomobject - append to $employmentStatus
$employmentStatus += [pscustomobject]#{
ADUser = $ID
Verified = $verified
}
}

Array and For loop for in Powershell

I am having two variables as below
$IP=#("IP1","IP2","IP3")
$Hostname=#("Host1","Host2","Host3")
I am working on some project in which there is Combobox and if the user selected the first choice then there is some command which should take variable as first IP and first hostname and when user select "ALL-DC" option, the command should be running on every IP and hostname one by one.
I am using below code
if ($Unity_ComboBox.text -match 'unit-02') {
$Unity=$IP[0]
$show=$hostname[0]
} elseif ($Unity_ComboBox.text -match 'zone-03'){
$Unity=$IP[1]
$show=$hostname[1]
} elseif ($Unity_ComboBox.text -match 'site-04'){
$Unity=$IP[2]
$show=$hostname[2]
} elseif ($Unity_ComboBox.text -match 'ALL DC'){
$Unity=$IP
$show=$hostname
}
foreach ($u in $Unity){
echo $u
echo $show
}
In individual its working fine but in case of ALL DC it's messing up with $show.
Could someone suggest me some better way to achieve this?
A one-liner using a better regex:
if($Unity_ComboBox.text-match".*DC-0([2-4]).*"){$Unity=$IP[$matches[1]-2];$show=$Hostname[$matches[1]-2]}elseif($Unity_ComboBox.text-match"ALL DC"){$Unity=$IP;$show=$Hostname}
Here the first regex is .*DC-0([2-4]).* capture the last computer index. If your form takes only the names nothing more then change it to ^DC-0([2-4])$.
Then assign two variables using the match.
If not, do the else

PowerShell WPF UI detect key combination

I am writing a UI to run in WinPE. I have everything working so far and am trying to make it so the user cannot close the window. I have made it full screen and disabled the close (X) button, but the user can still press Alt+F4 to close the UI. I have been able to make it so that if the user hits F8 the UI is in front so the user cannot Alt+Tab to it. I have read so many ways to do this but nothing covers it for PowerShell I am not sure how to implement it in the script. It is not running in a Runspace.
Here is what I have tried:
$altF4Pressed = {
[System.Windows.Input.KeyEventArgs]$Alt = $args[1]
if ( ($Alt.Key -eq 'System') -or ($Alt.Key -eq 'F4') ) {
if ($_.CloseReason -eq 'UserClosing') {
$UI.Cancel = $true
}
}
}
$null = $UI.add_KeyDown($altF4Pressed)
I have also read to do this (Disabling Alt+F4 in a Powershell form), but this does not work.
#disable closing window using Alt+F4
$UI_KeyDown = [System.Windows.Forms.KeyEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.KeyEventArgs]
if ($_.Alt -eq $true -and $_.KeyCode -eq 'F4') {
$script:altF4Pressed = $true;
}
}
$UI_Closing = [System.Windows.Forms.FormClosingEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosingEventArgs]
if ($script:altF4Pressed)
{
if ($_.CloseReason -eq 'UserClosing') {
$_.Cancel = $true
$script:altF4Pressed = $false;
}
}
Else{
[System.Windows.Forms.Application]::Exit();
# Stop-Process $pid
}
}
$UI.Add_Closing({$UI_Closing})
$UI.add_KeyDown({$UI_KeyDown})
I have also tried to do this:
$UI.Add_KeyDown({
$key = $_.Key
If ([System.Windows.Input.Keyboard]::IsKeyDown("RightAlt") -OR [System.Windows.Input.Keyboard]::IsKeyDown("LeftAlt")) {
Switch ($Key) {
"F4" {
$script:altF4Pressed = $true;
write-host "Alt+f4 was pressed"
}
}
}
})
It detects the first keyboard press, but not the next one while the other is pressed. I think I need to use a Keybinding event instead, just not sure how to implement that in Powershell at the App level (not input level). I read you can add a keybinding to XAML code itself, but how do that with Powershell to detect the key combination (Alt+F4) when the UI presents itself?
You don't have a replicate-able example and I don't want to write one just for this, but I think this may be of assistance.
$altF4Pressed = {
[System.Windows.Input.KeyEventArgs]$Alt = $args[1]
if ( ($Alt.Key -eq 'System') -or ($Alt.Key -eq 'F4') ) {
if ($_.CloseReason -eq 'UserClosing') {
$UI.Cancel = $true
$Alt.Handled = $true
}
}
}
$null = $UI.add_KeyDown([System.Windows.Input.KeyEventHandler]::new($altF4Pressed))
Also IIRC for WPF, you'll want to use System.Windows.Input not System.Windows.Forms, so the second snippet you posted may actually work if you change the namespace.
I found the detection of keys with forms or WPF to be well above my skill level. However, I was able to accomplish the same task with a different approach. I needed to do this with an install script in Windows 10.
What I did was disable both left/right alt keys and F4 during the time the script was running and revert the changes, delete the key, after completed.
With WinPE's registry getting "reset" on each reboot it's your call if you want to diable the change after you make it.
# Disable Left & Right ALT keys and F4
Set-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layout" -name "Scancode Map" -Value ([byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x38,0xe0,0x00,0x00,0x00,0x00))
To re-enable Alt + F4 simply delete the "Scancode Map" registry value and reboot again.
I found the answer here: http://www.edugeek.net/forums/windows-server-2008-r2/121900-disable-alt-f4.html

How to build dynamic link list with powershell and WPF?

I have an array of variable length with several website names und corresponding links. I will show them up in a Windows Forms based GUI.
The array will be read from an XML file, but it looks like this
$linklist = #(
("Site 1" , "https://link1.com"),
("Site 2" , "https://link2.com")
)
Then i have a Windows Forms window named "mainform" and create each item in there:
$Link1 = New-Object System.Windows.Forms.LinkLabel
$Link1.Text = $sitename
$Link1.Location = New-Object System.Drawing.Point(40,$calculatedPosition)
$Link1.add_Click({ start $sitelink })
$mainform.Controls.Add($Link1)
That could be done manually for each item in my array - so far, so easy, as log i have a fixed amount of items in my array.
But i like to do it dynamically, to handle arrays with customized content.
I tried to use dynamic variables, because every LinkLabel needs a different variable name.
I know, Dynamic variable names can be created by the New-Variable Cmdlet, but i have no idea, how manage this new variable for building a LinkLabel.
Thank you in advance for all your helpful ideas...
I would first create an ordered Hashtable out of your $linklist array-of-arrays to make things easier:
$linklist = #(
("Site 1" , "https://link1.com"),
("Site 2" , "https://link2.com")
)
# convert the array of arrays into an ordered Hashtable
$linkHash = [ordered]#{}
$linklist | ForEach-Object { $linkHash[$_[0]] = $_[1] }
Using that hashtable, creating the linklabels dynamically is not very hard to do:
$linkHash.GetEnumerator() | ForEach-Object {
$lnk = New-Object System.Windows.Forms.LinkLabel
$lnk.Text = $_.Name # set the name for the label
$lnk.Tag = $_.Value # store the link url inside the control's Tag property
$lnk.Location = New-Object System.Drawing.Point(40, $calculatedPosition)
# inside the scriptblock, $this refers to the LinkLabel control itself
$lnk.Add_Click({ Start-Process $this.Tag })
$mainform.Controls.Add($lnk)
$calculatedPosition += 15 # just a guess, you may want different vertical spacing
}

Index is out of range powershell

I have a script that builds a GUI with a list of printers that will be selected by the user.
These printers are also on a CSV file built like this :
Computer (name of the printer); IP
xxxx;x.x.x.x
I want to collect all the selected values in an array named x
Then I want to take every entry in the CSV that corresponds to the selected item and put it in another array named y
Finally I export the y array into a new CSV that will be used to install the printers on the domain.
I tried to go straight from second step to last step but i couldn't.
Here is the part of the code :
$OKButton.Add_Click({
foreach ($objItem in $objListbox.SelectedItems)
{$x += $objItem}
y=#()
for ($i=0; $i -lt $x.length; $i++)
{
$y[$i]=Import-Csv C:\Users\Administrateur\Desktop\Classeur33.csv | Where-Object {$_.Computer -eq $x[$i]}
}
$y > C:\Users\Administrateur\Desktop\allezjoue.csv
I've tried to do it with a 3 values x array in another script and it worked fine, but I really need to keep the listbox that allows the user to select the printers he wants.
Powershell always returns me "Index out of range"
I tried to put "$y=$x" so they have the same range, but when I do this it returns that I can't index in an object which has "System.string" type.
This is PowerShell and very object oriented. Use the objects and collections at hand.
Decriptive variable names are your friend.
$objListbox.SelectedItems is already a collection of objects.
Put it in a variable and loop through it with Foreach-Object aka foreach.
Import-CSV returns a collection of objects.
$Selection = $ObjListbox.SelectedItems
$printers = Import-CSV 'C:\Users\Administrateur\Desktop\Classeur33.csv'
foreach ($chosen in $Selection) {
$printers = $printers | where-object { $_.Computer -eq $Chosen.Name }
}
$printers | Export-CSV 'C:\Users\Administrateur\Desktop\allezjoue.csv' -NoTypeInformation
$Chosen.Name should be edited to conform with whatever objects you get in $Selection. You can test this by $ObjListbox.SelectedItems | Get-Member and examining the members for a property with the name of the item selected, then assuming the names match what's in your CSV, you should be good.
(bonus note) Storing data in and running as local admin is bad practice, even on your home lab. Your mistakes will have the power of local admin, and your users will not be able to run the scripts since the source/results files are in admin's desktop.

Resources