Write output from a function in powershell command window - function

I have a GUI and it calls a function depending on the button pressed. I would like for the output of the function to show in the powershell command window when I run the GUI. The code below contains 5 buttons, when I run the powershell script and click on any of the 5 buttons, nothing happens and it just hangs, until i close out of it.
# This is code for the GUI ▼
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '406,414'
$Form.text = "Post DC Patching Checker"
$Form.TopMost = $false
$Check_NetLogon = New-Object system.Windows.Forms.Button
$Check_NetLogon.text = "Check Netlogon"
$Check_NetLogon.width = 340
$Check_NetLogon.height = 50
$Check_NetLogon.location = New-Object System.Drawing.Point(15,17)
$Check_NetLogon.Font = 'Microsoft Sans Serif,10'
$Ping = New-Object system.Windows.Forms.Button
$Ping.text = "Ping Servers / Workstations"
$Ping.width = 340
$Ping.height = 50
$Ping.location = New-Object System.Drawing.Point(16,97)
$Ping.Font = 'Microsoft Sans Serif,10'
$ShowReplication = New-Object system.Windows.Forms.Button
$ShowReplication.text = "Show Replication"
$ShowReplication.width = 340
$ShowReplication.height = 50
$ShowReplication.location = New-Object System.Drawing.Point(16,183)
$ShowReplication.Font = 'Microsoft Sans Serif,10'
$DiskSpace = New-Object system.Windows.Forms.Button
$DiskSpace.text = "Disk Space"
$DiskSpace.width = 340
$DiskSpace.height = 50
$DiskSpace.location = New-Object System.Drawing.Point(15,267)
$DiskSpace.Font = 'Microsoft Sans Serif,10'
$CheckDNSsuffix = New-Object system.Windows.Forms.Button
$CheckDNSsuffix.text = "Check IP Configuration"
$CheckDNSsuffix.width = 340
$CheckDNSsuffix.height = 50
$CheckDNSsuffix.location = New-Object System.Drawing.Point(17,350)
$CheckDNSsuffix.Font = 'Microsoft Sans Serif,10'
$Form.controls.AddRange(#($Check_NetLogon,$Ping,$ShowReplication,$DiskSpace,$CheckDNSsuffix))
$Check_NetLogon.Add_Click({ CheckNetLogon })
$Ping.Add_Click({ PingServersAndWorkstations })
$ShowReplication.Add_Click({ ShowReplicationOnServers })
$DiskSpace.Add_Click({ ShowDiskSpace })
$CheckDNSsuffix.Add_Click({ ShowIPconfig })
# This is code for the GUI ▲
# Check the netlogon service ▼
function CheckNetLogon {
$netLogon =Get-Service -DisplayName netlogon
if ($netLogon.Status -eq "Running"){
$netLogon.DisplayName + 'Service is running already'}
}
# Check the netlogon service ▲
# Ping's several workstations and servers ▼
function PingServersAndWorkstations {
ping test2
ping test3
ping test4
ping test5
}
# Ping's several workstations and servers ▲
# Shows replication ▼
function ShowReplicationOnServers {
repadmin /showrepl
}
# Shows replication ▲
# Shows disk space ▼
function ShowDiskSpace {
Get-WmiObject -Class Win32_logicaldisk |
Select-Object -Property DeviceID, DriveType, VolumeName,
#{L='FreeSpaceGB';E={"{0:N2}" -f ($_.FreeSpace /1GB)}}
}
# Shows replication ▲
# Shows IP config ▼
function ShowIPconfig {
ipconfig
}
# Shows IP config ▲
[void]$Form.ShowDialog()

Add-Type -AssemblyName System.Windows.Forms
$formTest = New-Object system.Windows.Forms.Form
$formTest.Size = '406,414'
$formTest.StartPosition = 'CenterScreen'
$formTest.text = "Post DC Patching Checker"
$buttonPing = New-Object system.Windows.Forms.Button
$formTest.Controls.Add($buttonPing)
$buttonPing.text = "Ping Servers / Workstations"
$buttonPing.Size = '340,50'
$buttonPing.location = '16, 97'
$buttonPing.Font = 'Microsoft Sans Serif,10'
$buttonPing.Add_Click({
$this.Enabled = $false
'google.com', 'test1', 'test2' |
ForEach-Object{
Try{
Test-Connection $_ -Count 1 -ErrorAction Stop|
Out-String |
Write-Host -ForegroundColor green
}
Catch{
Write-Host $_ -fore Red
}
}
$this.Enabled = $true
})
$formTest.ShowDialog()

Related

PowerShell run a 'function' (not a script) from a Windows Form add_click

I want to run a defined PowerShell function on an add_click event in a Windows form in that script.
I've found lots of examples of how to call a .ps1 script from a click, but not how to call a function.
Use powershell command in add_click
So, here is my script in full. I am curious on two ways of outputting this information.
• Just open a PowerShell console and show the results in there (the pause will prevent it from closing), or
• In some kind of MessageBox (some more Windows Forms magic or techniques or tooltips, or whatever anyone thinks works well - such techniques are all very interesting to me).
function sysx {
$System = get-wmiobject -class "Win32_ComputerSystem"
$Mem = [math]::Ceiling($System.TotalPhysicalMemory / 1024 / 1024 / 1024)
$wmi = gwmi -class Win32_OperatingSystem -computer "."
$LBTime = $wmi.ConvertToDateTime($wmi.Lastbootuptime)
[TimeSpan]$uptime = New-TimeSpan $LBTime $(get-date)
$s = "" ; if ($uptime.Days -ne 1) {$s = "s"}
$uptime_string = "$($uptime.days) day$s $($uptime.hours) hr $($uptime.minutes) min $($uptime.seconds) sec"
"$Mem GB"
"Up: $uptime_string"
pause
}
# Load Assemblies
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName PresentationFramework # For MessageBox
$MyIcon = [Drawing.Icon]::ExtractAssociatedIcon((Get-Command powershell).Path)
# Create Primary form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Visible = $false
$objForm.WindowState = "minimized"
$objForm.ShowInTaskbar = $false
$objForm.add_Closing({ $objForm.ShowInTaskBar = $False })
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = $MyIcon
$objNotifyIcon.Text = "TrayUtility"
$objNotifyIcon.Visible = $true
$objContextMenu = New-Object System.Windows.Forms.ContextMenu
$ToggleMenuItemPS = New-Object System.Windows.Forms.MenuItem
$ToggleMenuItemPS.Index = 1
$ToggleMenuItemPS.Text = "New PowerShell"
if (Test-Administrator) { $ToggleMenuItemPS.Text = "New PowerShell (Admin)" }
$ToggleMenuItemPS.add_Click({
Start-Process PowerShell.exe
})
$ToggleMenuItemFunction = New-Object System.Windows.Forms.MenuItem
$ToggleMenuItemFunction.Index = 2
$ToggleMenuItemFunction.Text = "Mem and Uptime"
$ToggleMenuItemFunction.add_Click({
# some way to call the 'sysx' function in a PowerShell console, MessageBox, tooltip, etc...
})
# Create an Exit Menu Item
$ExitMenuItem = New-Object System.Windows.Forms.MenuItem
$ExitMenuItem.Index = 5
$ExitMenuItem.Text = "E&xit"
$ExitMenuItem.add_Click({
$objForm.Close()
$objNotifyIcon.visible = $false
})
# Add the Menu Items to the Context Menu
$objContextMenu.MenuItems.Add($ToggleMenuItemPS) | Out-Null
$objContextMenu.MenuItems.Add($ToggleMenuItemFunction) | Out-Null
$objContextMenu.MenuItems.Add($ExitMenuItem) | Out-Null
# Assign the Context Menu
$objNotifyIcon.ContextMenu = $objContextMenu
$objForm.ContextMenu = $objContextMenu
# Show the Form - Keep it open
$objForm.ShowDialog() | Out-Null
$objForm.Dispose()
You can just use only sysx there:
$ToggleMenuItemFunction.add_Click({sysx})
To show results in a message box tweak sysx a bit:
$wshshell = new-object -comobject Wscript.shell
$wsh.PopUp("$($Mem) GB`nUp: $($uptime_string)")
All code has scope, even for output. Your function will run, but since you are note telling the output where to go. I added the Test admin function since it was left out of your post.
I tested and validated the below and it works on my system, in a running PowerShell console/ISE/VSCode session. Meaning, it prints this output to the console (of course you can change that to send to a Message Box, et al) and start a new console as admin.
#region Begin code-behind logic
#
function Test-Administrator
{
([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::
GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
}
function Get-Sysx {
$System = get-wmiobject -class "Win32_ComputerSystem"
$Mem = [math]::Ceiling($System.TotalPhysicalMemory / 1024 / 1024 / 1024)
$wmi = gwmi -class Win32_OperatingSystem -computer "."
$LBTime = $wmi.ConvertToDateTime($wmi.Lastbootuptime)
[TimeSpan]$uptime = New-TimeSpan $LBTime $(get-date)
$s = ""
if ($uptime.Days -ne 1) {$s = "s"}
$uptime_string = "$($uptime.days) day$s $($uptime.hours) hr $($uptime.minutes) min $($uptime.seconds) sec"
"$Mem GB"
"Up: $uptime_string"
pause
}
#
#endregion End code behind logic
#region Begin GUI logic
#
# Load Assemblies
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName PresentationFramework # For MessageBox
$MyIcon = [Drawing.Icon]::ExtractAssociatedIcon((Get-Command powershell).Path)
# Create Primary form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Visible = $false
$objForm.WindowState = "minimized"
$objForm.ShowInTaskbar = $false
$objForm.add_Closing({ $objForm.ShowInTaskBar = $False })
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = $MyIcon
$objNotifyIcon.Text = "TrayUtility"
$objNotifyIcon.Visible = $true
$objContextMenu = New-Object System.Windows.Forms.ContextMenu
$ToggleMenuItemPS = New-Object System.Windows.Forms.MenuItem
$ToggleMenuItemPS.Index = 1
$ToggleMenuItemPS.Text = "New PowerShell"
if (Test-Administrator) { $ToggleMenuItemPS.Text = "New PowerShell (Admin)" }
$ToggleMenuItemPS.add_Click({
Start-Process PowerShell.exe
})
$ToggleMenuItemFunction = New-Object System.Windows.Forms.MenuItem
$ToggleMenuItemFunction.Index = 2
$ToggleMenuItemFunction.Text = "Mem and Uptime"
$ToggleMenuItemFunction.add_Click({
Get-Sysx | Out-Host
})
# Create an Exit Menu Item
$ExitMenuItem = New-Object System.Windows.Forms.MenuItem
$ExitMenuItem.Index = 5
$ExitMenuItem.Text = "E&xit"
$ExitMenuItem.add_Click({
$objForm.Close()
$objNotifyIcon.visible = $false
})
# Add the Menu Items to the Context Menu
[void]$objContextMenu.MenuItems.Add($ToggleMenuItemPS)
[void]$objContextMenu.MenuItems.Add($ToggleMenuItemFunction)
[void]$objContextMenu.MenuItems.Add($ExitMenuItem)
# Assign the Context Menu
$objNotifyIcon.ContextMenu = $objContextMenu
$objForm.ContextMenu = $objContextMenu
#endregaion End GUI logic
# Show the Form - Keep it open
[void]$objForm.ShowDialog()
$objForm.Dispose()
As for this... [I was hopeful that would work, but using Get-Sysx on its own does nothing.], if you are saying you are not in a PowerShell session when you call this, then that is because you did not start one before the call, so, nothing to write to. So, call a message box instead and the separate call to powershell.exe, and that pause is not needed since the messagebox will stay shown until dismissed.
$ToggleMenuItemFunction = New-Object System.Windows.Forms.MenuItem
$ToggleMenuItemFunction.Index = 2
$ToggleMenuItemFunction.Text = "Mem and Uptime"
$ToggleMenuItemFunction.add_Click({
[System.Windows.Forms.MessageBox]::Show($(Get-Sysx), 'Information', 0)
})

Is it possible to load all functions of a script before it runs?

I have a rather complex script, which follows the following steps;
-->Login (Ask user to enter admin details)
--->Start (Queries Ad for user creditals)
--->Progress (Creates a progress bar)
--->Search (Carries out the search for the data)
--->Question1 -(Yes - Select-Folder No - Create)
--->Select-Folder (Asks the user to create a file path for the document to be stored)
--->Go (Creates a csv from the results of the search)
--->Question2 (CSV - Result XLSX - Excel) (Asks the user if they wish to create a Xlsx file from the Csv)
--->Create (Checks to see if file path exists C:\temp\Server_shares if not creates it)
--->Done (creates csv at default location C:\temp\Server_shares)
--->Question2 (Asks the user if they wish to create a Xlsx file from the Csv)
--->Question2 -(CSV - Result XLSX - Excel) (Asks the user if they wish to create a Xlsx file from the Csv)
--->Result (User has chosen not to create a Xlsx notifys the user of the file path)
--->End (Closes script)
--->Excel (creates Xlsx from csv and stores it in either default or user defined location)
--->Delete (Deletes all remaingin Csv's from file path)
--->End (Closes script)
it seems that I keep get unhandelled exception errors everytime that I run the code.
I have found out that it runs then loads the functions, I need to run them all in memory so that when called each one will run without error.
I have tried creating varibles for each function, but this exactly the same as what I have already.
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void][System.Reflection.Assembly]::LoadWithPartialName ("Microsoft.VisualBasic")
$Script:ErrorActionPreference = "Stop"
$Script:Ma3 = "C:\Temp\Server_Shares\"
Function Get-Event{
Function Get-Login {
Clear-Host
#Write-Host "Please provide admin credentials (for example DOMAIN\admin.user and your password)"
$Global:Credential = Get-Credential
}
Function Get-Start{
#Get user credentials
$Cred = Get-Credential -Message "Enter Your Credentials (Domain\Username)"
if ($Cred -eq $Null)
{
Write-Host "Please enter your username in the form of Domain\UserName and try again" -BackgroundColor Black -ForegroundColor Yellow
Rerun
Break
}
#Parse provided user credentials
$DomainNetBIOS = $Cred.username.Split("{\}")[0]
$UserName = $Cred.username.Split("{\}")[1]
$Password = $Cred.GetNetworkCredential().password
Write-Host "`n"
Write-Host "Checking Credentials for $DomainNetBIOS\$UserName" -BackgroundColor Black -ForegroundColor White
Write-Host "***************************************"
If ($DomainNetBIOS -eq $Null -or $UserName -eq $Null)
{
Write-Host "Missing domain please type in the following format: Domain\Username" -BackgroundColor Black -ForegroundColor Yellow
Rerun
Break
}
# Checks if the domain in question is reachable, and get the domain FQDN.
Try
{
$DomainFQDN = (Get-ADDomain $DomainNetBIOS).DNSRoot
}
Catch
{
Write-Host "Error: Domain was not found: " $_.Exception.Message -BackgroundColor Black -ForegroundColor Red
Write-Host "Please make sure the domain NetBios name is correct, and is reachable from this computer" -BackgroundColor Black -ForegroundColor Red
Rerun
Break
}
#Checks user credentials against the domain
$DomainObj = "LDAP://" + $DomainFQDN
$DomainBind = New-Object System.DirectoryServices.DirectoryEntry($DomainObj,$UserName,$Password)
$DomainName = $DomainBind.distinguishedName
If ($DomainName -eq $Null)
{
Write-Host "Domain $DomainFQDN was found: True" -BackgroundColor Black -ForegroundColor Green
$UserExist = Get-ADUser -Server $DomainFQDN -Properties LockedOut -Filter {sAMAccountName -eq $UserName}
If ($UserExist -eq $Null)
{
Write-Host "Error: Username $Username does not exist in $DomainFQDN Domain." -BackgroundColor Black -ForegroundColor Red
Rerun
Break
}
Else
{
Write-Host "User exists in the domain: True" -BackgroundColor Black -ForegroundColor Green
If ($UserExist.Enabled -eq "True")
{
Write-Host "User Enabled: "$UserExist.Enabled -BackgroundColor Black -ForegroundColor Green
}
Else
{
Write-Host "User Enabled: "$UserExist.Enabled -BackgroundColor Black -ForegroundColor RED
Write-Host "Enable the user account in Active Directory, Then check again" -BackgroundColor Black -ForegroundColor RED
Rerun
Break
}
If ($UserExist.LockedOut -eq "True")
{
Write-Host "User Locked: " $UserExist.LockedOut -BackgroundColor Black -ForegroundColor Red
Write-Host "Unlock the User Account in Active Directory, Then check again..." -BackgroundColor Black -ForegroundColor RED
Rerun
Break
}
Else
{
Write-Host "User Locked: " $UserExist.LockedOut -BackgroundColor Black -ForegroundColor Green
}
}
Write-Host "Authentication failed for $DomainNetBIOS\$UserName with the provided password." -BackgroundColor Black -ForegroundColor Red
Write-Host "Please confirm the password, and try again..." -BackgroundColor Black -ForegroundColor Red
Rerun
Break
}
Else
{
Write-Host "SUCCESS: The account $Username successfully authenticated against the domain: $DomainFQDN" -BackgroundColor Black -ForegroundColor Green
}
Search
}
Function Rerun {
$Title = "Enter another set of Credentials?"
$Message = "Do you want to try another set of credentials?"
$Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Try Again?"
$No = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "End Script."
$Options = [System.Management.Automation.Host.ChoiceDescription[]]($Yes, $No)
$Result = $host.ui.PromptForChoice($Title, $Message, $Options, 0)
Switch ($Result)
{
0 {Get-Start}
1 {"End Script."}
}
}
Function Get-Progress{
Try{
{If (Test-Path $PC -ErrorAction Stop) {
Add-Type -assembly System.Windows.Forms
## -- Create The Progress-Bar
$ObjForm = New-Object System.Windows.Forms.Form
$ObjForm.Text = "Progress-Bar of searched folders"
$ObjForm.Height = 100
$ObjForm.Width = 500
$ObjForm.BackColor = "White"
$ObjForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle
$ObjForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
## -- Create The Label
$ObjLabel = New-Object System.Windows.Forms.Label
$ObjLabel.Text = "Starting. Please wait ... "
$ObjLabel.Left = 5
$ObjLabel.Top = 10
$ObjLabel.Width = 500 - 20
$ObjLabel.Height = 15
$ObjLabel.Font = "Tahoma"
## -- Add the label to the Form
$ObjForm.Controls.Add($ObjLabel)
$PB = New-Object System.Windows.Forms.ProgressBar
$PB.Name = "PowerShellProgressBar"
$PB.Value = 0
$PB.Style="Continuous"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 500 - 40
$System_Drawing_Size.Height = 20
$PB.Size = $System_Drawing_Size
$PB.Left = 5
$PB.Top = 40
$ObjForm.Controls.Add($PB)
## -- Show the Progress-Bar and Start The PowerShell Script
$ObjForm.Show() | Out-Null
$ObjForm.Focus() | Out-NUll
$ObjLabel.Text = "Starting. Please wait ... "
$ObjForm.Refresh()
Start-Sleep -Seconds 1
Out-Null
## -- Execute The PowerShell Code and Update the Status of the Progress-Bar
$result = (get-acl $pc).Access
$Counter = 0
ForEach ($Item In $Result) {
## -- Calculate The Percentage Completed
$Counter++
[Int]$Percentage = ($Counter/$Result.Count)*100
$PB.Value = $Percentage
$ObjLabel.Text = "Scanning $Folders For $criteria in $PC"
#$ObjLabel.Text = "Found $counter Pst's in $Search"
$ObjForm.Refresh()
Start-Sleep -Milliseconds 150
# -- $Item.Name
#"`t" + $Item.Path
$ObjForm.Close()
#Write-Host "`n"
Else {
#Write-Host
#Write-Host "`t Cannot Execute The Script." -ForegroundColor "Yellow"
#Write-Host "`t $Search Does Not Exist in the System." -ForegroundColor "Yellow"
#Write-Host
}
}
}
}
}
Catch{
Write-Host "Please enter a vaild path" -ForegroundColor Cyan
Search
}
}
Function Script:Get-Question {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$Form1 = New-Object System.Windows.Forms.Form
$Form1.ClientSize = New-Object System.Drawing.Size(200, 100)
$form1.topmost = $true
$Text = New-Object System.Windows.Forms.Label
$Text.Location = New-Object System.Drawing.Point(15, 15)
$Text.Size = New-Object System.Drawing.Size(200, 40)
$Text.Text = "Would you like to save the file to a custom location?"
$Form1.Controls.Add($Text)
#$ErrorActionPreference = "SilentlyContinue"
Function Button1
{
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = New-Object System.Drawing.Point(20, 55)
$Button1.Size = New-Object System.Drawing.Size(55, 20)
$Button1.Text = "Yes"
$Button1.add_Click({Get-Go -ErrorAction SilentlyContinue
$Form1.Close()})
$Form1.Controls.Add($Button1)
}
Function Button2
{
$Button2 = New-Object System.Windows.Forms.Button
$Button2.Location = New-Object System.Drawing.Point(80, 55)
$Button2.Size = New-Object System.Drawing.Size(55, 20)
$Button2.Text = "No"
$Button2.add_Click({Get-Create -ErrorAction SilentlyContinue
$Form1.Close()})
$Form1.Controls.Add($Button2)
}
Button1
Button2
[void]$form1.showdialog()
}
Function Select-FolderDialog{
param([string]$Description="Select Folder",[string] $RootFolder="Desktop")
[System.Reflection.Assembly]::LoadWithPartialName ("System.windows.forms") | Out-Null
Write-host "Please minimize the console to select a folder in which to save the results"
$objForm = New-Object System.Windows.Forms.FolderBrowserDialog
$objForm.Rootfolder = $RootFolder
$objForm.Description = $Description
$objForm.ShowNewFolderButton = $false
$Show = $objForm.ShowDialog()
If ($Show -eq "OK")
{
Return $objForm.SelectedPath
}
Else
{
Write-Error "Operation cancelled by user."
Exit
}
}
Function Get-Search{
Write-host "Please Minimize the console and enter the full folder path that you require permissions for" -ForegroundColor Green
$Script:PC = [Microsoft.VisualBasic.Interaction]::InputBox("Please enter the full path of the folder you wish to search", "Folder choice")
If ($PC -eq "")
{
Exit
}
Get-Progress
$Res = (get-acl $pc).Access
$Script:Gold = $Res| Select-object #{label = "User Groups";Expression = {$_.IdentityReference}},
#{label = "Rights";Expression = {$_.FileSystemRights}},
#{label = "Access";Expression = {$_.AccessControlType}}
Get-Question
}
Function Script:Get-Go{
$Form1.Close()
$FPath = Select-FolderDialog
$Folder = $FPath + "\" + [Microsoft.VisualBasic.Interaction]::InputBox ("Please select a folder to save the data to", "Path Choice") + "\"
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
"Please minimize the console to select a folder in which to save the results"
$Name = [Microsoft.VisualBasic.Interaction]::InputBox("Please choose a filename", "File Name Choice")
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$cfgOutpath = $Folder + "$Name"
if( -Not (Test-Path -Path $Folder ) )
{
New-Item -ItemType directory -Path $Folder |out-null
}
Else{
[System.Windows.MessageBox]::Show('The directory already exists','Error','Ok','Error')
}
$Gold | Export-Csv "$cfgOutpath.csv" -NoClobber -NoTypeInformation
Write-Host "File has been saved to $cfgOutpath.csv" -ForegroundColor Yellow
Get-Q2
}
##############################################
## Testing Phases ##
## Get-Start ##
## ##
##############################################
Search
Function Script:Get-Create {
$Form1.Close()
if( -Not (Test-Path -Path $Ma3 ) )
{
New-Item -ItemType directory -Path $Ma3 |out-null
}
Done
}
Function Script:Get-Done{
$PC2 = ($PC -split '\\')[-1]
$CSV = "C:\Temp\Server_Shares\User access for $PC2"
$cfgOutpath = $CSV
$Gold | Export-Csv "$cfgOutpath.csv" -NoClobber -NoTypeInformation
Get-Q2
}
Function Script:Get-Q2 {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$Form2 = New-Object System.Windows.Forms.Form
$Form2.ClientSize = New-Object System.Drawing.Size(200, 100)
$form2.topmost = $true
$Text = New-Object System.Windows.Forms.Label
$Text.Location = New-Object System.Drawing.Point(15, 15)
$Text.Size = New-Object System.Drawing.Size(200, 40)
$Text.Text = "Would you like to create an Xlsx document or leave it as csv?"
$Form2.Controls.Add($Text)
$ErrorActionPreference = "SilentlyContinue"
Function Button1
{
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = New-Object System.Drawing.Point(20, 55)
$Button1.Size = New-Object System.Drawing.Size(55, 20)
$Button1.Text = "CSV"
$Button1.add_Click({Get-Result
$Form2.Close()})
$Form2.Controls.Add($Button1)}
Function Button2
{
$Button2 = New-Object System.Windows.Forms.Button
$Button2.Location = New-Object System.Drawing.Point(80, 55)
$Button2.Size = New-Object System.Drawing.Size(55, 20)
$Button2.Text = "XLSX"
$Button2.add_Click({Get-Excel
$Form2.Close()})
$Form2.Controls.Add($Button2)
}
Button1
Button2
[void]$form2.showdialog()
}
Function Script:Get-Ans{
$Form2.Close()
Try{
Get-Excel
}
Catch{
Write-Host "Unable to create XSLX please check full path." -ForegroundColor Red
}
}
Function Script:Get-Result{
$Form2.Close()
Write-Host "File has been saved to $CSV.csv" -ForegroundColor Yellow
}
Function Script:Get-Excel{
$RD = $Ma3 + "*.csv"
$CsvDir = $RD
$csvs = dir -path $CsvDir # Collects all the .csv's from the driectory
$outputxls = "$Ma4.Xlsx"
$Excel = New-Object -ComObject excel.application
$Excel.displayAlerts = $false
$workbook = $excel.Workbooks.add()
# Loops through each CVS, pulling all the data from each one
foreach($iCsv in $csvs){
$iCsv
$WN = ($iCsv -Split "\\")[5]
$wn = ($WN -Split " ")[3]
$WN = $WN -replace ".{5}$"
$Excel = New-Object -ComObject excel.application
$Excel.displayAlerts = $false
$Worksheet = $workbook.worksheets.add()
$Worksheet.name = $WN
$TxtConnector = ("TEXT;" + $iCsv)
$Connector = $worksheet.Querytables.add($txtconnector,$worksheet.Range("A1"))
$query = $Worksheet.QueryTables.item($Connector.name)
$query.TextfileOtherDelimiter = $Excel.Application.International(5)
$Query.TextfileParseType =1
$Query.TextFileColumnDataTypes = ,2 * $worksheet.cells.column.count
$query.AdjustColumnWidth =1
$Query.Refresh()
$Query.Delete()
$Worksheet.Cells.EntireColumn.AutoFit()
$Worksheet.Rows.Item(1).Font.Bold = $true
$Worksheet.Rows.Item(1).HorizontalAlignment = -4108
$Worksheet.Rows.Item(1).Font.Underline = $true
$Workbook.save()
}
$Empty = $workbook.worksheets.item("Sheet1")
$Empty.Delete()
$Workbook.SaveAs($outputxls,51)
$Workbook.close()
$Excel.quit()
Write-Host "File has been saved to $outputxls" -ForegroundColor Yellow
Delete
}
Function Script:Delete{
get-childitem $MA3 -recurse -force -include *.txt | remove-item -force #Removes all txt files from final directory
get-childitem $MA3 -recurse -force -include *.csv | remove-item -force #Removes all CSV files from final directory
}
Write-Host "Finished"
}
Get-Event
#Excel-Write'
I know it's a lot of code, but in-order for anyone to replicate the probelm, you will need it all.
I want it to run, first time, everytime, at the moment it take 3-4 tries before its loaded each function into memory.
Put all of your functions into a PowerShell module file.
C:\Module.psm1
And then import that module before anything else in the script:
Import-Module C:\Module.psm1
For larger scripts this makes things more manageable as you can keep your long list of functions separate from the script that calls them.

PowerShell Function return value and further use of returned Variable

I don't get how the functions works in PowerShell. I tried it out a bit like C++ or C# but those languages I last tried out 7 years ago.
So as I try to work with the functions I have this function:
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
$location.ShowDialog()
write-host $location.FileName "in Function"
}
which determines the location of the csv that is loaded afterwards but as soon as I try to load the variable $location outside of the function it's NULL
With the write-host statement you can see it has the full path of the file.
As soon as I try to load it outside the function right after the code it won't work as its says its NULL
...
#Select which CSV
whichcsv
...
$CSV = Import-Csv -Path $location.FileName -UseCulture
$y = $CSV | Select Inventarnummer
$Filter = "Inventarnummer"
I tried to set my code in a int main(void) like in C-languages but I don't know how to handle this as well as there it would have been on the same scoope so it should have worked so but somehow it doesn't works as then I only get all in the console prompt but nothing ever happens
Full code for repro
#Importend:
#This is Work in Progress and not completed work.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
#Function Assembling
Function SaveWorkingdestination ()
{
$Saveworking = New-Object -Typename System.Windows.Forms.SaveFileDialog
$Saveworking.filter = "CSV (*.csv)| *.csv"
$Saveworking.ShowDialog()
return $Saveworking
}
function Savefaileddestination ()
{
$Savefailed = New-Object -Typename System.Windows.Forms.SaveFileDialog
$Savefailed.filter = "CSV (*.csv)| *.csv"
$Savefailed.ShowDialog()
return $Savefailed
}
function Compare ($location)
{
#work in progress
$CSV1 = Import-Csv -Path $location.FileName -UseCulture
$CSV2 = Import-Csv -Path $location.FileName -UseCulture
Compare-Object $CSV1 $CSV2 -property WhichColumn -IncludeEqual
return comparedfilename
}
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
$location.ShowDialog()
write-host $location.FileName "in Funktion"
}
function Checktrough ($y , $Filter,$Saveworking,$Savefailed)
{
foreach($n in $y)
{
try {
$Computer = [system.net.dns]::resolve($n.$Filter) | Select HostName,AddressList
$IP = ($Computer.AddressList).IPAddressToString
Write-Host $n.$Filter $IP
New-Object PSObject -Property #{IPAddress=$IP; Name=$n.$Filter} | Export-Csv $Saveworking.FileName -NoTypeInformation -Append
} catch {
Write-Host "$($n.$Filter) is unreachable."
New-Object PSObject -Property #{Name=$n.$Filter} | Export-Csv $Savefailed.FileName -NoTypeInformation -Append
}
}
}
#int main (void) #doesnt working so far
#{
#Select which option Form
$form = New-Object System.Windows.Forms.Form
$form.Text = "CSV Liste"
$form.Size = New-Object System.Drawing.Size(300,300)
$form.StartPosition = "CenterScreen"
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,195)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(150,195)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = "Welche CSV Liste soll geladen werden:"
$form.Controls.Add($label)
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,20)
$listBox.Height = 150
[void] $listBox.Items.Add("AS400 Computer")
[void] $listBox.Items.Add("AS400 Personalstamm")
[void] $listBox.Items.Add("ADComputer")
[void] $listBox.Items.Add("ADBenutzer")
#Formclosed
$form.Controls.Add($listBox)
$form.Topmost = $True
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
#Select which CSV
whichcsv
$x = $listBox.SelectedItem
switch ($x)
{
#Option 1
"AS400 Computer"
{
$CSV = Import-Csv -Path $location.FileName -UseCulture
$y = $CSV | Select Inventarnummer
$Filter = "Inventarnummer"
#SaveWorkingdestination($Saveworking)
#Export-Csv $Saveworking.FileName -NoTypeInformation -Append
}
#Option 2
"AS400 Personalstamm"
{
#not implemented yet
$y = $CSV | Select SpaltennameBzwFilter
$Filter = "Spaltennamme bzw Filter"
}
#Option 3
"ADComputer"
{
$CSV = Import-Csv -Path $location.FileName -Delimiter ','
$y = $CSV | Select Name
$Filter = "Name"
SaveWorkingdestination
Savefaileddestination
Checktrough
}
#Option 4
"ADBenutzer"
{
#not implemented yet
$y = $CSV | Select SpaltennameBzwFilter
$Filter = "Spaltenname bzw Filter"
}
}
}
#}
Maybe someone knows how to help me eather with getting the common code in something like the int main (void) from c languages or how to get a proper return from the functions... if there are still unclearitys i will clearly answer them and edit to get this working
edit: with script part it works perfectly fine so far, but now is there a format question to get some more structure in it. or is it common to have no wrap around in powershell scripts like you have in c languages with the
int main (void)
{
...
code
...
}
In your example:
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
$location.ShowDialog()
write-host $location.FileName "in Function"
}
$location, as assigned in the function body, is local to the function's scope. In PowerShell you can read variables from a parent scope, but writing to a variable creates a local copy by default, not persisted in the caller's scope.
What you want to do is return it's value from the scope, just like you would in a C-like language.
The caveat in PowerShell is that any value expression inside the function body that outputs anything will "bubble up" to the caller, not just the argument to return. So if you have statements inside the function body that return any value, make sure you capture or suppress that output:
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
[void]$location.ShowDialog() # <-- ShowDialog() returns a DialogResult, suppress it
return $location.FileName # return the filename property value
}
Then in the calling scope:
$CsvPath = whichcsv
$CsvPath will now contain the value of $location.Filename after the function returns
For more information about variable scoping in PowerShell, see the about_Scopes help topic
For more information about the behavior of return, see the about_Return help topic
For more information about the reason for this behavior in the first place, look into pipelines
You may have to use $scriptto change the scope level in which the variable can be used. To make the variable available outside the function do something like:
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
$location.ShowDialog()
$script:locationfilename = $location.FileName
}
write-host "$($locationfilename) in Function"
you have to return the object to the main program.
function whichcsv(){
$location = New-Object System.Windows.Forms.OpenFileDialog
$location.initialDirectory = $initialDirectory
$location.filter = "CSV (*.csv)| *.csv"
$location.ShowDialog()
return $location.FileName
}
$path= whichcsv
write-host $path

RunSpacePool hash table lookup

I'm putting together a powershell script that will use RunSpacePools to output a CSV file containing 1)ServerName, 2)SCCM Maintenance Window, 3)PingCheck, 4)LastRebootTimestamp.
I've got something working by using this amazing answer but my CSV file has blank lines and I'm stuck on getting the SCCM Maintenance Window into the CSV.
I'm unsure of how to complete the SCCM Maintenance Window lookup then add it to the output of the $Job.Result or could I just add it into the $ScriptBlock and let the RunSpacePool very quickly complete the lookup.
The blank CSV line is ,, and some lines don't have the extra blank line.
-edit, my thinking is now to perform the SCCM window lookup then simply pass that into the runspacepool as another param/argument.
IF(Get-Command Get-SCOMAlert -ErrorAction SilentlyContinue){}ELSE{Import-Module OperationsManager}
"Get Pend reboot servers from prod"
New-SCOMManagementGroupConnection -ComputerName ProdSCOMServer
$AlertData = get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot detected on the ConfigMgr 2012 Client'" | Select NetbiosComputerName
"Get Pend reboot servers from test"
#For test information
New-SCOMManagementGroupConnection -ComputerName TestSCOMServer
$AlertData += Get-SCOMAlert -Criteria "Severity = 1 AND ResolutionState < 254 AND Name = 'Pending Reboot detected on the ConfigMgr 2012 Client'" | Select NetbiosComputerName
"Remove duplicates"
$AlertDataNoDupe = $AlertData | Sort NetbiosComputerName -Unique
$Global:table = #{}
"Populate hash table"
$MaintenanceWindow = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$MaintenanceWindow | ForEach-Object {$Global:table[$_.Computername] = $_.CollectionName}
$scriptblock = {
Param([string]$server)
#Try getting SCCM Maintenance Window
$SCCMWindow = IF($Global:table.ContainsKey($server)){
$SCCMWindow = $table[$server]
} Else { $SCCMWindow = "Not Found!"}
$PingCheck = Test-Connection -Count 1 $server -Quiet -ErrorAction SilentlyContinue
IF($PingCheck){$PingResults = "Alive"}
ELSE{$PingResults = "Dead"}
Try{$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $server -ErrorAction Stop
$LastReboot = [Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime)
$LastReboot.DateTime}
Catch{$LastReboot = "Access Denied!"}
[PSCustomObject]#{
Server=$server
Ping=$PingResults
LastReboot=$LastReboot
}#end custom object
}#script block end
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(100,100)
$RunspacePool.Open()
$Jobs =
foreach ( $item in $AlertDataNoDupe )
{
$Job = [powershell]::Create().
AddScript($ScriptBlock).
AddArgument($item.NetbiosComputerName)
$Job.RunspacePool = $RunspacePool
[PSCustomObject]#{
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
Write-Host 'Working..' -NoNewline
Do {
Write-Host '.' -NoNewline
Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host ' Done! Writing output file.'
Write-host "Output file is d:\scripts\runspacetest5.csv"
$(ForEach ($Job in $Jobs)
{ $Job.Pipe.EndInvoke($Job.Result) }) |
Export-Csv d:\scripts\runspacetest5.csv -NoTypeInformation
$RunspacePool.Close()
$RunspacePool.Dispose()
Not sure if this is the best way but I ended up using the following which presents a problem in the event there are two entries in MaintenanceWindow2.csv because it returns System.Object[]
$scriptblock = {
Param([string]$server)
$csv = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$window = $csv | where {$_.Computername -eq "$server"} | % CollectionName
$SCCMWindow = IF ($window){$window}ELSE{"NoDeadline"}
}

Call a function from a script and have it run in the original script

I have two seperate powershell scripts that need to be run at the same time. One is a kiosk, and the other is a script that detects idle time and calls a function in the kiosk script. The function is designed to close the kiosk and reopen it to clear any form data. The only issue is that it doesn't close the original kiosk instead opens a new instance.
I have attemped to combine the scripts into one but have had no luck in getting it to work.
Here is the idle time script:
Add-Type #'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PInvoke.Win32 {
public static class UserInput {
[DllImport("user32.dll", SetLastError=false)]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO {
public uint cbSize;
public int dwTime;
}
public static DateTime LastInput {
get {
DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
return lastInput;
}
}
public static TimeSpan IdleTime {
get {
return DateTime.UtcNow.Subtract(LastInput);
}
}
public static int LastInputTicks {
get {
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
GetLastInputInfo(ref lii);
return lii.dwTime;
}
}
}
}
'#
for ( $i = 1)
{Write-Verbose ("Idle for " + [PInvoke.Win32.UserInput]::IdleTime)
Start-Sleep -Seconds 1
if ([PInvoke.Win32.UserInput]::IdleTime -gt "00:00:10.0000000")
{Stop-Process -processname kiosk*
C:\kiosk.exe MakeNewForm
}
The idle time is set to 10 seconds for testing. It will be longer of course when in production.
And here is the kiosk script:
Set-ExecutionPolicy Unrestricted
$URL = "www.website.com"
$event = {$Browser.Print()}
Add-Type -AssemblyName System.Windows.Forms
#Browser Refresh
Function MakeNewForm {
$mainForm.close()
$mainForm.dispose()
Start-Sleep -Milliseconds 100
# WinForm
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Text = " Internet Explorer"
$mainform.WindowState = "maximized"
$mainForm.ControlBox = $false
$mainForm.FormBorderStyle = "none"
$mainForm.MinimumSize = "1024, 768"
$mainForm.Top = $true
# Back
$back = New-Object System.Windows.Forms.Button
$back.Size = "55, 55"
$back.Location = "875, 0"
$back.BackgroundImage = [System.Drawing.Image]::FromFile('C:\back_button.jpg')
$back.Add_Click({$Browser.GoBack()})
$mainForm.Controls.Add($back)
# Forward
$forward = New-Object System.Windows.Forms.Button
$forward.Size = "55, 55"
$forward.location = "953, 0"
$forward.BackgroundImage = [System.Drawing.Image]::FromFile('C:\forward_button.jpg')
$forward.Add_Click({$browser.GoForward()})
$mainForm.Controls.Add($forward)
# Go Home
$gohome = New-Object System.Windows.Forms.Button
$gohome.Size = "55, 55"
$gohome.location = "75, 0"
$gohome.BackgroundImage = [System.Drawing.Image]::FromFile('C:\home_button.jpg')
$gohome.Add_Click({$browser.URL= "$URL"})
$mainForm.Controls.Add($gohome)
# Print
$button = New-Object System.Windows.Forms.Button
$button.Size = "54, 55"
$button.Location = "0, 0"
$button.BackgroundImage = [System.Drawing.Image]::FromFile('C:\print.jpg')
$button.Add_Click($event)
$mainForm.Controls.Add($button)
# Browser
$Browser = New-Object System.Windows.Forms.WebBrowser
$Browser.IsWebBrowserContextMenuEnabled = $false
$Browser.URL = $URL
$Browser.Location = "0, 0"
$Browser.Dock = "fill"
$mainForm.Controls.Add($Browser)
}
# WinForm
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Text = " Internet Explorer"
$mainform.WindowState = "maximized"
$mainForm.ControlBox = $false
$mainForm.FormBorderStyle = "none"
$mainForm.MinimumSize = "1024, 768"
$mainForm.Top = $true
# Back
$back = New-Object System.Windows.Forms.Button
$back.Size = "55, 55"
$back.Location = "875, 0"
$back.BackgroundImage = [System.Drawing.Image]::FromFile('C:\back_button.jpg')
$back.Add_Click({$Browser.GoBack()})
$mainForm.Controls.Add($back)
# Forward
$forward = New-Object System.Windows.Forms.Button
$forward.Size = "55, 55"
$forward.location = "953, 0"
$forward.BackgroundImage = [System.Drawing.Image]::FromFile('C:\forward_button.jpg')
$forward.Add_Click({$browser.GoForward()})
$mainForm.Controls.Add($forward)
# Go Home
$gohome = New-Object System.Windows.Forms.Button
$gohome.Size = "55, 55"
$gohome.location = "75, 0"
$gohome.BackgroundImage = [System.Drawing.Image]::FromFile('C:\home_button.jpg')
$gohome.Add_Click({$browser.URL= "$URL"})
$mainForm.Controls.Add($gohome)
# Print
$button = New-Object System.Windows.Forms.Button
$button.Size = "54, 55"
$button.Location = "0, 0"
$button.BackgroundImage = [System.Drawing.Image]::FromFile('C:\print.jpg')
$button.Add_Click($event)
$mainForm.Controls.Add($button)
# Browser
$Browser = New-Object System.Windows.Forms.WebBrowser
$Browser.IsWebBrowserContextMenuEnabled = $false
$Browser.URL = $URL
$Browser.Location = "0, 0"
$Browser.Dock = "fill"
$mainForm.Controls.Add($Browser)
$mainForm.ShowDialog() | Out-Null
I have tried . .\idle.ps1, do{}while(), combining the the idle script to the end of the kiosk script but nothing has worked when combining them.
Is there is a way to either combine these or have the idle actually call the function and have it run in the kiosk script?
Thanks in advance for any replies or advice!
Your method is sound. What you need is to write the process id of the kiosk script into something persistent, i.e a text file. This can then be used for the idle script to reference from and kill the original process if needed.The current process id of a Powershell session can be acessed via the built-in variable $pid.
To save the process id of the current session
$pid | Out-File -FilePath c:\kiosk-pid.txt
To access it later and use it to kill the process
Get-Process -PID ([int32](Get-Content -Path C:\kiosk-pid.txt)) | Stop-Process
I completely forgot to mention that these would be compliled to run as an .exe. What I eneded up going with is with the stop-process -processname kiosk* and it is working flawlessly now. Thank you again MFT for pointing me in the correct direction. I will update the above script to show the changes.
if ([PInvoke.Win32.UserInput]::IdleTime -gt "00:00:10.0000000")
{Stop-Process -processname kiosk*
C:\kiosk.exe MakeNewForm