Cross talk threads to allow for file system access in powershell - multithreading

Reference: Runspace for button event in powershell
https://www.foxdeploy.com/blog/part-v-powershell-guis-responsive-apps-with-progress-bars.html
So, I believe my issue is that PowerShell is unable to access the memory space of the file system, from within the memory block, of my thread, is there a way to solve this, to access the file system, from a multi-threaded application?
Back Story:
So, I run a program, that calls upon "code"/command, from the command prompt, (*.exe) (Robocopy) to copy files from a server, to a group of computers, at a time. We have a classroom environment, at my work, so I have my setup, in a way, that I have a folder, per room. I keep a list of all our addresses (static), for each room, in their perspective folders. We have an update, from our developers, that we need to push to all of the rooms. We need to run a slow push, as to not disturb the production environment. It's proprietary, so we can't use a/any typical solution(s), like Microsoft SCCM. So, I created a script to push to the rooms. while it does work, it's not a smooth operation. I'm not actually the one pushing the update, because of the slow process, of updating. I'm just trying to make a stable smooth-running package, for the person, who is going to be doing it. My code works, outside of the thread, (I) tested it, I know it works.
So how I came my conclusion of knowing, that my code works outside of the thread. (The picture) I followed the same setup, with my code, (A button click event inside of a thread, running the form). Placed the actual working code, (tried, and tested, before making a Thread, for the interface, after completing backend operation code testing.)
("Region Boe's Addition") referring to Boe Prox, (from the link)
In his, he is updating from a command line/powershell window, via a function run inside a thread. I'm running an event from a button, inside of a thread and trying to run a separate thread, for the click event(s). Outside of the thread. The event works fine, but inside, it doesn't work, at all..
Basic Code:
// Multi thread, thread for the $form, and thread for the event (as per referenced link)
$var = [PowerShell]::Create().AddScript({ button.Add_Click{
$var = [PowerShell]::Create().AddScript{<Thread><Robocopy></Thread>}
})

Needed the "Start-Process" -Wait command to allow for the listbox, to be updated in-between copies, to confirm installation, through each step in the loop.
$choice = $comboBox.SelectedItem
# $drive = Get-Location
if(!(Test-Path -PathType Container -Path "L:\$choice"))
{
# New-Item -ItemType "container" -Path . -Name $choice
New-Item -ItemType "Directory" -Path . -Name $choice
}
# $folder = $_
# Where is it being stored at?
[System.IO.File]::ReadLines("Y:\$choice\IPs.txt") | foreach {
ping -a -n 2 -w 2000 $_ | Out-Null
Test-Connection -Count 2 -TimeToLive 2 $_ | Out-Null
if($?)
{
RoboCopy /Log:"L:\$folder\$_.log" $source \\$_\c$\tools
RoboCopy /Log+:"L:\$folder\$folder-MovementLogs.log" $source \\$_\c$\tools
Start-Process -Wait "P:\psexec.exe" -ArgumentList "\\$_ -d -e -h -s cmd /c reg import C:\tools\dump.reg"
# Copy-Item -LiteralPath Y:\* -Destination \\$_\c$\tools
$listBox.Items.Add($_)
}
}

Related

Issue with Start-ThreadJob ScriptBlock Unable to find powershell script

I am using Start-ThreadJob and ScriptBlock to execute a powershell script in a new thread. It works fine on my local but on the preprod server, I am getting an error.
Code Block where I am initiating a new thread
Start-ThreadJob -InputObject $fileType -ScriptBlock {
./Functions/Download-FilesFromFTP.ps1 $args[0] $args[1] $args[2] $args[3] $args[4] $args[5]
} -ArgumentList $ftpServer,$user,$password,$completeSourceFolder,$completeStagingFolderPath,$completeLogFolderPath
As mentioned earlier, this code block works perfectly on my local. On Preprod env I get the following error when I display jobs using Get-Jobs command.
Powershell version on my local
Powershell version on preprod server
The version of the module ThreadJob is same on both servers
Start-ThreadJob runs the new thread with the same current location as the caller, which is unrelated to where the executing script is located.
If you want to refer to a file relative to the script's own location, use the automatic $PSScriptRoot variable, and refer to it in the thread script block via the $using: scope:
Start-ThreadJob -InputObject $fileType -ScriptBlock {
& "$using:PSScriptRoot/Functions/Download-FilesFromFTP.ps1" #args
} -ArgumentList $ftpServer,$user,$password,$completeSourceFolder,$completeStagingFolderPath,$completeLogFolderPath
Note the use of #args in order to also pass all positional arguments, reflected in the automatic $args array, through as individual arguments to the target script via splatting.

Disable/enable completely input devices (mouse+keyboard+touchpad) in Windows10

I'm trying to disable/enable input devices in my laptop (win10), automatically (.reg file, python code etc)
I tried to use DevCon but after a lot of attempts it didn't work out for my touchpad and keyboard (I tried to disable, remove).
I searched the web and the solutions don't completely disable the devices (for example: Ctrl+Alt+Delete is not blocked).
I work on a windows 10 Laptop, You can assume that you have admin Privileges.
Have to check for Keyboard but for Mouse and Touchpad, you can use some Powershell commands to check and find out the actual device Classes and InstanceIDs and then turn off with an Admin elevated Powershell prompt.
The InstanceIDs of Mouse and Touchpad is different on different brands and types of Laptops, but first you can identify those with their Classes such as HIDClass. To get that fire up Powershell prompt(you've already tried REG and Python, so assuming you'll be okay with Powershell too (.ps1)) and run this command:
Get-PnpDevice | Where-Object {$_.Class -eq 'HIDClass'}
This may show 2 or 3 entries of which 1 belongs to Mouse and other to Touchpad, this would be a bit of trial and error, you have to pick any InstanceID to make filter more target specific and fire-up admin-elevated Powershell (search Powershell and click on "Run As Administrator") and run Disable-PnpDevice method like below(if InstanceId contains "ACPI"):
Get-PnpDevice | Where-Object {$_.Class -eq 'HIDClass' -and $_.InstanceId -like 'ACPI*'} | Disable-PnpDevice -Confirm:$false
This will disable Touchpad(in mine(Lenovo) it did disabled it) and then you can try out another InstanceID and disable the Mouse too in the same way. Voila !! both are turned off now.
If you prefer this in .ps1 script format then you need a self-elevating script which can enable/disable the devices without any halts, save this code in .ps1 file and then right-click > Run with PowerShell:
$Loc = Get-Location
"Security.Principal.Windows" | % { IEX "( [ $_`Principal ] [$_`Identity ]::GetCurrent() ).IsInRole( 'Administrator' )" } | ? {
$True | % { $Arguments = #('-NoProfile','-ExecutionPolicy Bypass','-NoExit','-File',"`"$($MyInvocation.MyCommand.Path)`"","\`"$Loc\`"");
Start-Process -FilePath PowerShell.exe -Verb RunAs -ArgumentList $Arguments; } }
Get-PnpDevice | Where-Object {$_.Class -eq 'HIDClass' -and $_.InstanceId -like 'ACPI*'} | Disable-PnpDevice -Confirm:$false
Read-Host
Note: If you in case disable the wrong or undesired device than for enabling it, in the same admin-elevated Powershell window run the same filter command (the Get-PnpDevice with filters) and replace Disable-PnpDevice with Enable-PnpDevice.
Let me know in comments if you still face issue with above commands.
Untested, but calling BlockInput() should do what you want. It blocks both keyboad and mouse input. It is however defined in user32.dll so you will need to use ctypes to access it:
import ctypes
ctypes.windll.user32.BlockInput(True)

Stop IIS express process based on site name

The site im working on got 3 individual sites running on the IIS.
When I make changes to on particular library I need to restart the site using it. The way I do that now is by manually rightclicking the IIS Express icon in the system tray, and then clicking 'Stop site', and after that I execute the debugging..
I would like to make that part automatic, so when ever i start debugging it will stop that particular site.
If I don't stop it, then it will just reuse the current running site, but if I stop it, then it will restart it..
Is it event posible? I know how to find the PID, but I don't get the name of the site behind the PID..
I put together this script in PowerShell:
$site = 'Webapplication' # replace this by your site name (not case sensitive)
$process = Get-CimInstance Win32_Process -Filter "Name = 'iisexpress.exe'" | ? {$_.CommandLine -like "*/site:`"$site`"*" }
if($process -ne $null)
{
Write-Host "Trying to stop $($process.CommandLine)"
Stop-Process -Id $process.ProcessId
Start-Sleep -Seconds 1 # Wait 1 second for good measure
Write-Host "Process was stopped"
} else
{
Write-Host "Website was not running"
}
Modify the first line to replace the site name with yours. Save this
file as stopiis.ps1 on your project folder (not the solution folder).
Now, on Solution Explorer, right-click and choose properties
On the left side, choose Build Events
Put this on 'Pre-Build event command line' so it will run before compiling:
echo powershell -File "$(ProjectDir)stopiis.ps1"
powershell -File "$(ProjectDir)stopiis.ps1"
Note: you do not need to run Visual Studio in Administrative mode because IISExpress.exe run under your account

Automatic deployment of solutions with PowerShell

I have a folder, containing several solutions for a SharePoint application, which I want to add and install. I want to iterate over the elements in the folder, then use the Add-SPSolution. After that I want to do a check if the solutions are done deploying, before using the Install-SPSolution. Here is a snippet that I am currently working on:
# Get the location of the folder you are currently in
$dir = $(gl)
# Create a list with the .wsp solutions
$list = Get-ChildItem $dir | where {$_.extension -eq ".wsp"}
Write-Host 'DEPLOYING SOLUTIONS...'
foreach($my_file in Get-ChildItem $list){Add-SPSolution -LiteralPath $my_file.FullName}
Write-Host 'SLEEP FOR 30 SECONDS'
Start-Sleep -s 30
Write-Host 'INSTALLING SOLUTIONS...'
foreach($my_file in Get-ChildItem $list){Install-SPSolution -Identity $my_file.Name -AllWebApplications -GACDeployment}
Is there a way to check if the deployment is finished, and it is ready to start installing the solutions?
You need to check the SPSolution.Deployed property value in a loop - basic solution looks like this:
do { Start-Sleep 2 } while (!((Get-SPSolution $name).Deployed))
The Deploying SharePoint 2010 Solution Packages Using PowerShell article contains more details and this comment discusses a potential caveat.

How can I suspend a Hyper-V VM from a linux machine?

I've set up an NMS system on a machine running Ubuntu that responds to various UPS events by calling a Perl script to go through all of our VMWare hosts and suspend all of the VMs. VMWare was smart and provided a set of Perl modules which made this relatively easy. We also have three Hyper-V hosts, however, and I can't seem to find a way to control them that isn't specific to some Microsoft technology (e.g. a PowerShell script).
I'm hoping somebody could suggest a way to control the Hyper-V hosts from a linux box. I'd rather it didn't involve using Wine, but I'm willing to go that route if there's nothing else that will work.
I found an ugly way to do it, but at least it doesn't require anything to be installed or configured on the VM host.
First I got a utility called winexe, which lets you open a terminal connection to a windows machine.
Then I wrote a long an ugly Perl script to pipe some PowerShell code to the machine to suspend any running machines:
sub hv_suspend_host {
my $host = $_[0];
my $code = <<'END';
echo '===BEGIN'
$query = "SELECT * FROM Msvm_ComputerSystem WHERE EnabledState != 3 AND EnabledState != 32769" #Exclude off and saved VMs
$VMs = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
foreach ($VM in $VMs) {
if ($VM.name -ne $VM.ElementName) { # Exclude the host itself
if ($VM.RequestStateChange(32769).ReturnValue -eq 4096) { # Put the VM in a saved state
# It worked, log success
} else {
# It didn't, log failure
}
}
}
echo '===END'
exit
END
my $recv;
run(["winexe", '-U', "DOMAIN/$win_user%$win_pass", '--interactive=0', "//$host", 'powershell -command -'], \$code, \$recv);
$recv =~ tr/\r//d; # Convert to UNIX line endings
$recv =~ /===BEGIN\n(.+)===END/s; # Now recv contains anything you logged
}
You might have to mess with this a bit to get it to work. I had to hack out some of the implementation-specific things, but I left in part of the output capturing code. This requires global variables named $win_user and $win_pass containing administrator account login info for the target VM host. It also requires that you use IPC::Run.
Hyper-V can be managed remotely using the WMI interfaces. There is a WMI Client for Linux, which should allow you to make the relevant API calls to manage Hyper-V. I have not had to do this myself, but the specific WMI calls are available at Microsoft: https://msdn.microsoft.com/en-us/library/hh850319%28v=vs.85%29.aspx

Resources