Powershell popup to close applications - powershell

I want to create powershell file that will create a popup to warn someone that an application will close automatically in 15 mins unless they extend a session. It should then kill the application unless they extend the session.
I’m completely new to this and this is all I have mustered trying it with the notepad application.
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::YesNoCancel
$MessageIcon = [System.Windows.MessageBoxImage]::Error
$MessageBody = "Can we close notepad?"
$MessageTitle = "Using Notepad"
$Result = [System.Windows.MessageBox]::Show($MessageBody, $MessageTitle, $ButtonType, $MessageIcon)
Write-Host "Your choice is $Result"
Get-Process Notepad | Foreach-Object { $_.CloseMainWindow() | Out-Null }

Mathias, if you change this to a switch it works like you want where it takes Yes to close the window, but No and cancel leave it open
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::YesNoCancel
$MessageIcon = [System.Windows.MessageBoxImage]::Error
$MessageBody = "Can we close notepad?"
$MessageTitle = "Using Notepad"
$Result = [System.Windows.MessageBox]::Show($MessageBody, $MessageTitle, $ButtonType, $MessageIcon)
switch($Result){
"Yes" {
write-host "You selected $Result"
Get-Process notepad | Foreach-Object { $_.CloseMainWindow() | Out-Null } | Stop-Process -Force
}
"No" {
Write-Host "You selected $Result"
}
"Cancel" {
Write-Host "You selected $Result"
}
}

Related

Powershell - copy RTF to cliboard - multiple line text not working

I used to use a tiny utility called ScriptMan, it would sit in the system tray, and with two clicks it would copy the contents of my selected RTF to the clipboard. I haven't found an equivalent that works on Windows 10 and doesn't monitor the clipboard constantly, so I've tried to write my own in PowerShell. It's worked - almost...
After spending a bit of (way too much of!!) my afternoon/evening on StackOverflow and several other places, I've come up with the following script:
function RTF_to_Clipboard {
$ScriptName = $this.Tag
Write-Host "ScriptName = $ScriptName"
$LabelOutput.Text = "Button $ScriptName clicked"
$ScriptFilePath = "$ScriptsFolder$ScriptName"
Write-Host "$ScriptFilePath = $ScriptFilePath"
#https://stackoverflow.com/questions/65543792/how-to-write-contents-of-an-rtf-file-to- windows-clipboard-with-one-liner
Add-Type -AssemblyName System.Windows.Forms
$rtf = Get-Content -Path $ScriptFilePath
$IsRTF = ($rtf | Select-String '\{\\rtf1' -Quiet)
Write-Host "File $ScriptFilePath" (&{If($IsRTF) {"is"} Else {"IS NOT"}}) "an RTF file."
#[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Rtf)
#[Windows.Forms.Clipboard]::SetDataObject($rtf, $true)
[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Text)
Write-Host $rtf
}
This function is launched by a GUI:
# https://theitbros.com/powershell-gui-for-scripts/
$ScriptsFolder = 'C:\Users\me\OneDrive\Documents\IT\scripts\'
$Scripts = #(
'script 1 - multiline text.rtf',
'script 2 - single line text.rtf',
'script 3 - multiline RTF.rtf'
)
Add-Type -assembly System.Windows.Forms
#Now create the screen form (window) to contain elements:
$main_form = New-Object System.Windows.Forms.Form
#Set the title and size of the window:
$main_form.Text ='Copy Scripts to Clipboard'
$main_form.Width = 600
$main_form.Height = 400
#If the elements on the form are out of window bounds, use the AutoSize property to make the form automatically stretch.
$main_form.AutoSize = $true
#Create button and label for each item in the $Scripts array
$loopcount = 0
foreach($Command in $Scripts)
#for ($loopcount=0; $loopcount -lt $Scripts.count; $loopcount++)
{
$YPos = 25 * $loopcount + 5
$loopcount++
# $Command = $Scripts[$loopcount]
#Now put the button on the form:
Echo "Create button $loopcount for function $Command"
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(0,$YPos)
$Button.Size = New-Object System.Drawing.Size(60,23)
$Button.Text = "Load"
$Button.Tag = $Command
$Button.Add_Click({
$LabelOutput.Text = "$Command clicked"
Write-Host "$Command clicked"
RTF_to_Clipboard
# RTF_to_Clipboard -ScriptName $Command
})
$main_form.Controls.Add($Button)
#Create a label element on the form:
$Label = New-Object System.Windows.Forms.Label
$Label.Text = $Command
$Label.Location = New-Object System.Drawing.Point(70,$YPos)
$Label.AutoSize = $true
$main_form.Controls.Add($Label)
}
$LabelOutput = New-Object System.Windows.Forms.Label
$LabelOutput.Text = "No command selected yet"
$LabelOutput.Location = New-Object System.Drawing.Point(0,150)
$LabelOutput.AutoSize = $true
$main_form.Controls.Add($LabelOutput)
#Now you can display the form on the screen.
$main_form.ShowDialog()
The form buttons appear to be working, but not copying the text/RTF to the clipboard.
As you can guess from the script names, I have 3 test scripts:
Multi-line plain text
Single-line plain text
Rich Text Format
You can see also from the # remming lines out that I've tried a few methods that others have said they used:
[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Rtf)
This method pastes the plain text into MS word, but doesn't paste anything into NotePad++ or PuTTY (where I need them. The RTF is correct in MS Word (tables preserved), but throws a bunch of errors about "This is not a valid style name."
[Windows.Forms.Clipboard]::SetDataObject($rtf, $true)
The second method copies the single-line text to the clipboard no worries, and it pastes OK in NotePad++, PuTTY and MS Word. The multi-line text and RTF buttons appear to put nothing on the clipboard.
[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Text)
With the last method, the single-line scripts work fine, but the multi-line script all ends up on one line. RTF files get their raw data converted to text - i.e.: a tonne of RTF formating code displayed as plain text; again, all on one line:
{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang2057\deflangfe2057\themelang2057\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi
\froman\fcharset0\fprq2{*\panose 02020603050405020304}Times New
Roman;}{\f0\fbidi \froman\fcharset0\fprq2{*\panose
02020603050405020304}Times New Roman;} {\f37\fbidi
\fswiss\fcharset0\fprq2{*\panose
020f0502020204030204}Calibri;}{\f38\fbidi
\fswiss\fcharset0\fprq2{*\panose
00000000000000000000}Tahoma;}{\flomajor\f31500\fbidi
\froman\fcharset0\fprq2{*\panose 02020603050405020304}Times New
Roman;} etc., etc., etc., etc., etc., etc., etc., etc., etc.....
When there is nothing pasting from the clipboard, the PowerShell console is still showing the text value of the files when I call Write-Host $rtf, so I know that $rtf = Get-Content -Path $ScriptFilePath worked each time.
Has anyone got any pointers as to how to get the multi-line text copied correctly to the clipboard at least?
I should mention that some of my multi-line scripts (the files that I'm trying to copy to the clipboard) are bash scripts, not sure if their content is breaking the PowerShell clipboard, e.g.:
alias pingAll="while true; do datenow=$(date -D "hh:mm:ss"|cut
-c12-19);echo -ne $datenow; echo -ne " -> VPN: "; if ping -q -c 1 -w 5 $VPN_Server_IP>/dev/null; then echo -ne "Up "; else echo -ne " Down"; fi; echo -ne " -> Server A: "; if ping -q -c 1 -w 5
$ServerA_IP>/dev/null; then echo -ne "Up "; else echo -ne "
Down"; fi; echo -ne " -> Server B: "; if ping -q -c 1 -w 5
$ServerB_IP>/dev/null ; then echo -ne "Up "; else echo -ne "
Down"; fi; echo ; sleep 5; done"
Version = ";grep "<version" /etc/stationInfo.xml |cut -f2 -d">"|cut
-f1 -d"<"; echo -ne "Firmware Revision = ";grep "<revision" /etc/stationInfo.xml |cut -f2 -d">"|cut -f1 -d"<"; echo -ne "Firmware
Grade = ";grep "<grade" /etc/stationInfo.xml |cut -f2 -d">"|cut -f1
-d"<"; echo -ne "Firmware Date = ";grep "<date" /etc/stationInfo.xml |cut -f2 -d">"|cut -f1 -d"<"'alias fwver='echo -ne "Firmware
I also tried Set-Clipboard -Path "c:\temp\script.rtf", but it pastes nothing into NotePad++, and pastes the text or RTF into MS word as an embedded object that needs to be opened to edit, not as inline text.
Any clues how to get this working?
Thanks for your help!
Cheers.
Here's the solution that I'm running now, works just as I want it (would be nice to hide the PowerShell output window, but not a problem).
I've removed the array of script paths, and have my script scan the script folder and grab every *.txt and *.rtf in the script folder instead. To add a new script, you just need to copy it to the script folder, no changes to this script required.
As I had too many scripts to fit on one screen, I now start a new column of buttons when half of them are displayed.
If a script contains "#", its label is set to red font, to make my email addresses stand out so I can grab them quickly. (Yes, I use this for even short things I need to type regularly. :) )
# Scott Critchley
# 16/02/2022
# With help from https://stackoverflow.com/questions/71149309/powershell-copy-rtf-to-cliboard-multiple-line-text-not-working?noredirect=1#comment125769903_71149309
# and https://stackoverflow.com/questions/65543792/how-to-write-contents-of-an-rtf-file-to-windows-clipboard-with-one-liner
# Detect if we're running in the ISE or not, so can exit the shell when done, but not when in ISE
# https://www.sapien.com/forums/viewtopic.php?t=14149#:~:text=Another%20way%20to%20determine%20what,'Windows%20PowerShell%20ISE%20Host'.
[bool]$ISE = 0
switch ($Host.Name){
ConsoleHost { }
PrimalScriptHostImplementation { }
'Windows PowerShell ISE Host' {
Write-Host 'Running in Windows PowerShell ISE Host'
$ISE = 1
}
}
#https://community.spiceworks.com/topic/1710213-hide-a-powershell-console-window-when-running-a-script?page=1#entry-5990631
$Script:showWindowAsync = Add-Type -MemberDefinition #"
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"# -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru
Function Show-Powershell()
{
$null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 10)
}
Function Hide-Powershell()
{
$null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 2)
}
function RTF_to_Clipboard {
# param (
# [string]$ScriptName
# )
$ScriptName = $this.Tag
Write-Host "ScriptName = $ScriptName"
$LabelOutput.Text = "Button: $ScriptName clicked"
$ScriptFilePath = "$ScriptsFolder$ScriptName"
Write-Host "ScriptFilePath = $ScriptFilePath"
#https://stackoverflow.com/questions/65543792/how-to-write-contents-of-an-rtf-file-to-windows-clipboard-with-one-liner
Add-Type -AssemblyName System.Windows.Forms
$rtf = Get-Content -Path $ScriptFilePath -Raw
$IsRTF = ($rtf | Select-String '\{\\rtf1' -Quiet)
Write-Host "File $ScriptFilePath" (&{If($IsRTF) {"is"} Else {"IS NOT"}}) "an RTF file."
if ( $IsRTF )
{
[Windows.Forms.Clipboard]::SetText($rtf, [System.Windows.Forms.TextDataFormat]::Rtf)
}
else
{
[Windows.Forms.Clipboard]::SetDataObject($rtf, $true)
}
}
# https://theitbros.com/powershell-gui-for-scripts/
$ScriptsFolder = 'C:\Users\me\OneDrive\Documents\IT\scripts\'
Add-Type -assembly System.Windows.Forms
#Now create the screen form (window) to contain elements:
$main_form = New-Object System.Windows.Forms.Form
#Set the title and size of the window:
$main_form.Text ='Copy Scripts to Clipboard'
$main_form.Width = 60
$main_form.Height = 40
#If the elements on the form are out of window bounds, use the AutoSize property to make the form automatically stretch.
$main_form.AutoSize = $true
#Create button and label for each item in the $Scripts array
$loopcount = 0
$XPos = 0
$YPosCount = 0
# All files in folder from #https://www.winhelponline.com/blog/how-to-change-shortcut-lnk-targets-in-bulk-using-script/
# As implemented in Update Shortcuts.ps1
$ScriptCount = (gci $ScriptsFolder\* -Include *.txt, *.rtf |Measure-Object ).Count
Write-Host "Total Script Count = $ScriptCount"
gci $ScriptsFolder\* -File -Include *.txt, *.rtf | foreach {
$Command = $_.Name
$YPos = 25 * $YPosCount + 5
$YPosCount++
$loopcount++
if ($loopcount -eq ([math]::floor($ScriptCount/2))){
Write-Host "XPos = $XPos"
$XPos = 370
$YPosCount = 0
$YPosMax = $YPos
}
#Now put the button on the form:
Echo "Create button $loopcount for function $Command at $XPos,$YPos"
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size($XPos,$YPos)
$Button.Size = New-Object System.Drawing.Size(60,23)
$Button.Text = "Load"
$Button.Tag = $Command
$Button.Add_Click({
$LabelOutput.Text = "$Command clicked"
Write-Host "$Command clicked"
RTF_to_Clipboard
})
$main_form.Controls.Add($Button)
#Create a label element on the form:
$Label = New-Object System.Windows.Forms.Label
$Label.Text = $Command.Substring(0,$Command.Length - 4)
$LabelXPos = $XPos + 70
$Label.Location = New-Object System.Drawing.Point($LabelXPos,$YPos)
$Label.AutoSize = $true
if ($Command -like '*#*') {
$Label.ForeColor = 'Red'
$Label.BorderStyle = 'Fixed3D'
}
$main_form.Controls.Add($Label)
}
$YPos = $YPosMax + 5
$LabelOutput = New-Object System.Windows.Forms.Label
$LabelOutput.Text = "No command selected yet"
$LabelOutput.Location = New-Object System.Drawing.Point(0,$YPos)
$LabelOutput.AutoSize = $true
$main_form.Controls.Add($LabelOutput)
if($ISE)
{
Show-Powershell
}
else
{
Hide-Powershell
}
#Now you can display the form on the screen.
$main_form.ShowDialog()
if(-Not $ISE)
{[Environment]::Exit(1)}

Powershell FolderBrowserDialog behaving differently from shell to ise

When I run the below code in Powershell ISE everything runs fine, but when I run it from Powershell Shell I get a lot of errors about icons.
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |Out-Null
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.Description = "Select a folder"
$foldername.rootfolder = "MyComputer"
$foldername.SelectedPath = $initialDirectory
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
$folder
Example of Errors:
2021-10-19 15:44:43,174 ERROR - 4600 Unable to get icon for location (The operation completed successfully.): C:\Users\adalton\Pictures\Camera Roll
2021-10-19 15:44:43,176 ERROR - 4600 Unable to get icon for location (The operation completed successfully.): C:\Users\adalton\Pictures\Saved Pictures
C:\Users\adalton\Pictures\Saved Pictures
adalton,
Note that the ISE has things automatically loaded that the CMD version does not and must be loaded by the programmer. The modified code below (read the comments in the code) works on both the ISE and CMD versions of PowerShell 5.1.19041.1237
Add-Type -AssemblyName System.windows.forms
$Folder = #() #Set up blank array so += works!
$TermMsg = {
[Windows.Forms.MessageBox]::Show($Message,
"$Title",
[Windows.Forms.MessageBoxButtons]::OK ,
[Windows.Forms.MessageBoxIcon]::Information) | Out-Null}
If ($host.Name -eq 'ConsoleHost' -or
$host.Name -eq 'Visual Studio Code Host') {
try{ <#+------------------------------------------------+
| Check that the proper assemblies are loaded |
| Required for PS Console and Visual Code, while |
| only Systems.Windows.Forms needed for PSISE! |
+------------------------------------------------+
#>
$ATArgs = #{AssemblyName = "PresentationCore",
"PresentationFramework",
"WindowsBase"
ErrorAction = 'Stop'}
Add-Type #ATArgs
}
catch {
$Title = "Program Terminated:"
$Message =
"Failed to load Windows Presentation Framework" +
" and/or other assemblies required for this program!"
& $TermMsg
Exit
}
} #End If ($host.Name...
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.Description = "Select a folder"
#Root Folder is restricted see here:
#https://learn.microsoft.com/en-us/dotnet/api/system.environment.specialfolder?view=net-5.0
$foldername.rootfolder = [System.Environment+SpecialFolder]'MyComputer'
#$foldername.SelectedPath = $initialDirectory
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
$folder

Check multiple file paths exists before running script

I need to check that I have all files before running the rest of my powershell script.
What is the best way to go through the below code and after it has found each file required, continue with the rest of my script. Or if any of the files are not found, I send an email alert along with exiting the script.
# collect log one
if (Test-Path $logone) {
$one = Import-Csv -Path $logone
Write-Host "Log one found"
}
else {
#Send email "Log one not found!"
Write-Host "Log one not found!"
}
# collect log two
if (Test-Path -Path $logtwo) {
$two = Import-Csv -Path $logtwo
Write-Host "Log two found"
}
else {
#Send email "Log two not found!"
Write-Host "Log two not found!"
}
# collect log three data
if (Test-Path -Path $logthree) {
$three = Import-Csv -Path $logthree
Write-Host "Log three found"
}
else {
#Send email "Log three not found!"
Write-Host "Log three not found!"
}
Would I just add the following code below what I have:
if (Test-Path $logone) -and (Test-Path $logtwo) -and (Test-Path $logthree) {
# continue with the rest of my code
}
else {
Write-Host "Script exited with error"
Exit
}
Is there a cleaner way of doing this?
Based on I.T Delinquent's answer here an example of how to achieve this:
$logOne = "C:\Temp\log1"
$logTwo = "C:\Temp\log2"
$logThree = "C:\Temp\log3"
$mandatoryLogs = $logOne,$logTwo,$logThree
$errors = #()
$count = 0
$logs = #{}
foreach($log in $mandatoryLogs){
$count++
if(Test-Path $log){
$logs["log$count"] = Import-Csv -Path $log
Write-Host "Log $count found!"
}else{
$errors += "$log is missing"
}
}
if($errors){
$body = #"
Dear Admin,
The following errors occured: $($errors | Out-String)
Regards
"#
try{
Send-MailMessage -Body $body # -To -From etc etc
}catch{
throw $_
}
throw "Quiting because of errors, mail has been sent"
}else{
Write-Host "Continuing script" -ForegroundColor Green
}
Next you can access each of the logs in the following way:
$logs['log1']
Or:
Write-Host "$($logs['log3'].randomPropertyWhichExistsInTheCSV)"
Hope this helps!

Speed Powershell Script Up

I am looking for way to speed up my Powershell script. I have a script that returns the manager Employee ID and manager name based on a .txt file that has the samaccountnames for each user under that manager. The problem is the list is very long, about 1400+ names and the script is taking forever to run. Here is my script. It works, just looking for a way to speed it up:
cls
If (!(Get-Module -Name activerolesmanagementshell -ErrorAction SilentlyContinue))
{
Import-Module activerolesmanagementshell
}
Write-host $("*" * 75)
Write-host "*"
Write-host "* Input file should contain just a list of samaccountnames - no header row."
Write-host "*"
Write-host $("*" * 75)
$File = Read-Host -Prompt "Please supply a file name"
If (!(test-path $File))
{
Write-host "Sorry couldn't find the file...buh bye`n`n"
exit
}
get-content $File | %{
$EmpInfo = get-qaduser -proxy -Identity $_ -IncludedProperties employeeid,edsva_SSCOOP_managerEmployeeID
# Check if we received back a Manager ID - if yes, get the Manager's name
# If not, set the Manager Name to "NONE" for output
If ($($EmpInfo.edsva_SSCOOP_managerEmployeeID).length -gt 2)
{
# Get the Manager's name from AD
$($EmpInfo.edsva_SSCOOP_managerEmployeeID)
$ManagerName = $(Get-QADUser -SearchAttributes #{employeeid=$($EmpInfo.edsva_SSCOOP_managerEmployeeID)} | select name).name
If (!$ManagerName)
{
$ManagerName = "NONE"
}
# Add the Manager name determined above (or NONE) to the properties we'll eventually output
$EmpInfo | Add-Member -MemberType NoteProperty -Name ManagerName -Value $ManagerName
}
Else
{
$EmpInfo.edsva_SSCOOP_managerEmployeeID = "NONE"
}
# Output user samaccountname edsva_SSCOOP_managerEmployeeID and ManagerName to a file
$EmpInfo | select samaccountname,edsva_SSCOOP_managerEmployeeID,ManagerName | export-csv "C:\Users\sfp01\Documents\Data_Deletion_Testing\Script_DisaUser_MgrEmpID\Disabled_Users_With_Manager.txt" -NoTypeInformation -Append
} # End of file processing loop
Ok, first things first... asking your user to type in a file name. Give them a nice friendly dialog box with little effort. Here's a function I keep on hand:
Function Get-FilePath{
[CmdletBinding()]
Param(
[String]$Filter = "All Files (*.*)|*.*|Comma Seperated Values (*.csv)|*.csv|Text Files (*.txt)|*.txt",
[String]$InitialDirectory = $home,
[String]$Title)
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
$OpenFileDialog.Title = $Title
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
Then you can do:
$File = Get-FilePath -Filter 'Text Files (*.txt)|*.txt|All Files (*.*)|*.*' -InitialDirectory "$home\Desktop" -Title 'Select user list'
That doesn't speed things up, it's just a quality of life improvement.
Secondly, your 'can't find the file' message will appear as the window closes, so the person that ran your script probably won't see it. Towards that end I have a function that I use to pause a script with a message.
Function Invoke-Pause ($Text){
[reflection.assembly]::LoadWithPartialName('Windows.Forms')|out-null
If($psISE){
[Windows.Forms.MessageBox]::Show("$Text", "Script Paused", [Windows.Forms.MessageBoxButtons]"OK", [Windows.Forms.MessageBoxIcon]"Information") | ?{(!($_ -eq "OK"))}
}Else{
Write-Host $Text
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
}
With that you can get a message to the user, and then close the script so the user knows what happened. This function works in both the PowerShell console, as well as in the PowerShell ISE. In the console you get a text message that you define, and then a 'Press any key to continue...' message, and it waits for the user to press a key. In the ISE it pops up a window with your message, and waits for the user to click the OK button before proceeding. You could do something like:
If(!(Test-Path $File)){Invoke-Pause "Sorry couldn't find the file...buh bye";exit}
Now to get on to speeding things up!
You have more than one employee per manager right? So why look up the manager more than once? Setup a hashtable to keep track of your manager info, and then only look them up if you can't find them in the hashtable. Before your loop declare $Managers as a hashtable that just declares that 'NONE' = 'NONE', then inside the loop populate it as needed, and then reference it later.
Also, you are appending to a file for each user. That means PowerShell has to get a file lock on the file, write to it, close the file, and release the lock on it... over and over and over and over... Just pipe your users down the pipeline and write to the file once at the end.
Function Get-FilePath{
[CmdletBinding()]
Param(
[String]$Filter = "All Files (*.*)|*.*|Comma Seperated Values (*.csv)|*.csv|Text Files (*.txt)|*.txt",
[String]$InitialDirectory = $home,
[String]$Title)
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
$OpenFileDialog.Title = $Title
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
cls
If (!(Get-Module -Name activerolesmanagementshell -ErrorAction SilentlyContinue))
{
Import-Module activerolesmanagementshell
}
Write-host $("*" * 75)
Write-host "*"
Write-host "* Input file should contain just a list of samaccountnames - no header row."
Write-host "*"
Write-host $("*" * 75)
$File = Get-FilePath -Filter 'Text Files (*.txt)|*.txt|All Files (*.*)|*.*' -InitialDirectory "$home\Desktop" -Title 'Select user list'
If (!(test-path $File))
{
Write-host "Sorry couldn't find the file...buh bye`n`n"
exit
}
$Managers = #{'NONE'='NONE'}
Get-Content $File | %{
$EmpInfo = get-qaduser -proxy -Identity $_ -IncludedProperties employeeid,edsva_SSCOOP_managerEmployeeID
Switch($EmpInfo.edsva_SSCOOP_managerEmployeeID){
{$_.Length -lt 2} {$EmpInfo.edsva_SSCOOP_managerEmployeeID = 'NONE'}
{$_ -notin $Managers.Keys} {
$MgrLookup = Get-QADUser -SearchAttributes #{employeeid=$EmpInfo.edsva_SSCOOP_managerEmployeeID} |% Name
If(!$MgrLookup){$MgrLookup = 'NONE'}
$Managers.add($EmpInfo.edsva_SSCOOP_managerEmployeeID,$MgrLookup)
}
}
Add-Member -InputObject $EmpInfo -NotePropertyName 'ManagerName' -NotePropertyValue $Managers[$EmpInfo.edsva_SSCOOP_managerEmployeeID] -PassThru
} | select samaccountname,edsva_SSCOOP_managerEmployeeID,ManagerName | Export-Csv "C:\Users\sfp01\Documents\Data_Deletion_Testing\Script_DisaUser_MgrEmpID\Disabled_Users_With_Manager.txt" -NoTypeInformation -Append

Start Process and read StandardOutput

I am looking for a way to start a process in a new console window or the same window and catch its output, I can open process in new window using:
[Diagnostics.Process]::Start("C:\test.exe","-verbose -page")
This will open new window witch I can interact with but I cannot redirect output for it (output I mean whole interaction with window like key press and messages)
So I thought I can try with:
Start-Transcript -path "C:\test.txt" -append
$ps = new-object System.Diagnostics.Process
$ps.StartInfo.Filename = "C:\test.exe"
$ps.StartInfo.Arguments = " -verbose -page"
$ps.StartInfo.RedirectStandardOutput = $True
$ps.StartInfo.UseShellExecute = $false
$ps.start()
while ( ! $ps.HasExited ) {
write-host = $ps.StandardOutput.ReadToEnd();
}
Now here I get get output but without interaction, so I need some sort of option or procedure to start this process in same or different console and catch my interaction with it to file.
It is important as application sometimes asks for press any key and if Ill launch it in background it will never ask it because this app measures console window and checks id output will fit?
Is such thing possible?
You should be able to do
myprocess.StandardInput.WriteLine("some input");
or
myprocess.StandardInput.Write(" ");
As I was having big problem with this kind of interactivity in powershell, I mean run console app and press key, I finally got it right and I am shearing my solution.
Press button to continue function:
Function Press-Button
{
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait('~');
}
Start process, log whole transaction and press to continue:
Function Run-Tool
{
Start-Transcript -path "C:\test.txt" -append
$ps = new-object System.Diagnostics.Process
$ps.StartInfo.Filename = "C:\test.exe"
$ps.StartInfo.Arguments = " -verbose -page"
$ps.StartInfo.RedirectStandardInput = $true;
$ps.StartInfo.UseShellExecute = $false
$ps.start()
while ( ! $ps.HasExited )
{
Start-Sleep -s 5
write-host "I will press button now..."
Press-Button
}
Stop-Transcript
}
No matter how you do this it will be unreliable in an active system:
Function Run-Tool {
Start-Transcript -path "C:\test.txt" -append
Add-Type -AssemblyName System.Windows.Forms
$ps = new-object System.Diagnostics.Process
$ps.StartInfo.Filename = "C:\test.exe"
$ps.StartInfo.Arguments = " -verbose -page"
$ps.StartInfo.RedirectStandardInput = $true;
$ps.StartInfo.UseShellExecute = $false
$ps.start()
do{
Start-Sleep -s 5
write-host "I will press button now..."
[System.Windows.Forms.SendKeys]::SendWait('~');
}
until($ps.HasExited)
Stop-Transcript
}