Powershell Wait-process until installation complete [duplicate] - powershell

As part of a backup operation, I am running the 7zip command to compress a folder into a single .7z file. No problems there as I am using the InVoke-WMIMethod.
Example:
$zip = "cmd /c $irFolder\7za.exe a $somedirectory.7z $somedirectory"
"InVoke-WmiMethod -class Win32_process -name Create -ArgumentList $zip -ComputerName $remotehost"
My problem comes in as my script continues, the 7za.exe process hasn't completed. I am then attempting to copy the item off of the remote system and it is either incomplete or fails.
Can someone point me in the direction to figure out how to identify if the 7za.exe process is still running, wait until it is dead, then proceed with the rest of my script?
I can grasp pulling the process from the remote system via...
get-wmiobject -class Win32_Process -ComputerName $remotehost | Where-Object $_.ProcessName -eq "7za.exe"}
Not sure how to turn that into usable info for my issue.
Answer UPDATE: (thx to nudge by #dugas)
This will do it with some feedback for those that need it...
do {(Write-Host "Waiting..."),(Start-Sleep -Seconds 5)}
until ((Get-WMIobject -Class Win32_process -Filter "Name='7za.exe'" -ComputerName $target | where {$_.Name -eq "7za.exe"}).ProcessID -eq $null)

You can invoke the Wait-Process cmdlet on the remote computer with the Invoke-Command cmdlet. Example:
$process = Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList notepad -ComputerName RemoteComputer
Invoke-Command -ComputerName RemoteComputer -ScriptBlock { param($processId) Wait-Process -ProcessId $processId } -ArgumentList $process.ProcessId
Since you mentioned using Invoke-Command is not an option, another option is polling.
Example:
$process = Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList notepad -ComputerName hgodasvccr01
$processId = $process.ProcessId
$runningCheck = { Get-WmiObject -Class Win32_Process -Filter "ProcessId='$processId'" -ComputerName hgodasvccr01 -ErrorAction SilentlyContinue | ? { ($_.ProcessName -eq 'notepad.exe') } }
while ($null -ne (& $runningCheck))
{
Start-Sleep -m 250
}
Write-Host "Process: $processId is not longer running"

You should be able to do it with a do... while loop that just sleeps until the process is finished.
do {
"waiting"
start-sleep 10
} while (gwmi -class win32_process -ComputerName $remotehost | Where ProcessName -eq "7za.exe")

Related

Powershell and TPM how to manage bitlocker?

I am trying to script a powershell function manage-bde.exe (bitlocker) to add a key protector to systems without TPM. For some reason GPO is not working. I have not had any luck getting powershell to add the protector remotely. I can log on to the endpoint and use the built in wizard to encrypt and save the key to our repository but for some reason remote automated scripting eludes me. My question is really more of guidance. Can powershell only be used, to remotely manage systems with TPM? I have bitlocker enabled and encrypted on systems without but I have had to do it manually.
Start-Transcript -Path ".\bitlockertranscript.txt" -Force
foreach ($Computer in $List) {
if (test-Connection -ComputerName $Computer -Count 1 -Quiet ) {
Get-ADComputer -Identity $Computer -Property * | Select Name,OperatingSystem
Get-WmiObject -class Win32_Tpm -namespace root\CIMV2\Security\MicrosoftTpm -computername $Computer | fl IsActivated_InitialValue, IsEnabled_InitialValue, IsOwned_InitialValue
$BitLocker = Get-WmiObject -ComputerName $Computer -Namespace Root\cimv2\Security\MicrosoftVolumeEncryption -Class Win32_EncryptableVolume
$id = $BitLocker.GetKeyProtectors(3).volumekeyprotectorid | Select -First 1
manage-bde.exe -cn $Computer -protectors -adbackup c:
manage-bde.exe -on C: -cn $Computer
Invoke-GPUpdate -Target $computer
} else
{"No Connection to $Computer"
}
}
Stop-Transcript

Get a process's owners, send them messagebox and then kill the processes

I trying to get a list of users, who are running an application, for instance chrome.exe. Then send them a message and set the countdown timer for 300 seconds and after this I need to stop these processes. I have tried the following PowerShell.
$owners = #{}
gwmi win32_process | % {
$owners[$_.handle] = $_.getowner().user
}
get-process | select processname,Id,#{l="Owner";e={$owners[$_.id.tostring()]}}
Answer was closed. below you can see a solution:
$USERLIST = (Get-WmiObject -Class Win32_Process -Filter "Name='tsappldr.exe'" -ComputerName "SRUS270137C").getowner().user
ForEach ($computer in $USERLIST) {
$CmdMessage = {C:\windows\system32\msg.exe $computer /v 'Cancel your session'} $CmdMessage | Invoke-Expression
}
Start-Sleep -m 5 #time-out for 5 minutes
get-process tsappldr, tpedrte | stop-process -force
Chrome usually start several process even if only one tab is opened. You'll need to select the unique user name to avoid several pop-up messges. You can use:
(Get-WmiObject -Class Win32_Process -Filter "Name='chrome.exe'" -ComputerName "TargetPCName").getowner().user|select -Unique
Could you provide more info about the scenario that you have?
Thanks for everyone, who helped me, so i wrote current code:
$USERLIST = (Get-WmiObject -Class Win32_Process -Filter "Name='tsappldr.exe'" -ComputerName "SRUS270137C").getowner().user
ForEach ($computer in $USERLIST) {
$CmdMessage = {C:\windows\system32\msg.exe $computer /v 'Cancel your session'} $CmdMessage | Invoke-Expression
}
Start-Sleep -m 5 #time-out for 5 minutes
get-process tsappldr, tpedrte | stop-process -force
And it works correct!
With this code you can get the the process owner
$(Get-WmiObject -Class Win32_Process -Filter "Name='chrome.exe'" -ComputerName client).GetOwner()
And with this code you can display a messagebox.
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Message")
And with Stop-process you can kill a process
When you have some errors with your code post it here, I will try to help

Stopping & Restarting Services Remotely Using Set-Service

I've got a list of 10-15 services that I routinely need to restart on 6 servers. I have a script that calls a list of services, then calls a list of the servers, and then stops all the services:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Set-Service -Status Stopped
I then have another separate script to start them up again:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Set-Service -Status Running
I've checked around and can't seem to find a way of putting this into a single script. As I understand, Set-Service only has the ability to Stop, Start & Pause services, not restart them at the same time.
Any ideas? I might be missing something completely obvious.
To restart services simply use Restart-Service:
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
Get-Service -Name $Services -ComputerName $Machines | Restart-Service
Since according to the comments PowerShell v6 has removed support for remote access from the *-Service cmdlets you need to resort to Invoke-Command for remote execution when running v6 or newer, like this:
Invoke-Command -Computer $Machines -ScriptBlock {
Get-Service -Name $using:Services -ErrorAction SilentlyContinue |
Restart-Service
}
or like this:
Invoke-Command -Computer $Machines -ScriptBlock {
Restart-Service $using:Services -ErrorAction SilentlyContinue
}
Another option would be WMI:
$fltr = ($Services | ForEach-Object { 'Name="{0}"' -f $_ }) -join ' or '
Get-WmiObject Win32_Service -Computer $Machines -Filter $fltr | ForEach-Object {
$_.StopService()
$_.StartService()
}
I am with Ansgar, this should work
$Services = Get-Content -Path "C:\Powershell\Services.txt"
$Machines = Get-Content -Path "C:\Powershell\Machines.txt"
foreach ($service in $services){
foreach ($computer in $Machines){
Invoke-Command -ComputerName $computer -ScriptBlock{
Restart-Service -DisplayName $service}
}
}
it is a little messy but should give you a starting point
Sorry I forgot to take time to explain what is going on, so you import each of your txt docs and then it will process for each service and each computer and restart the services.
You can try this single liner command:
Get-Content .\services.txt | %{Get-WmiObject -Class Win32_Service -ComputerName (Get-Content .\computers.txt) -Filter "Name='$_'"} | %{$_.StopService()}; Get-Content .\services.txt | %{Get-WmiObject -Class Win32_Service -ComputerName (Get-Content .\computers.txt) -Filter "Name='$_'"} | %{$_.StartService()}

Powershell Kill all processes except system

In powershell, I would like to kill all processes for all users, except explorer and processes used by the system
This is where I am including the errors that are given:
$Cred = Get-Credential;
Invoke-Command -ComputerName localhost -Credential $Cred -ScriptBlock { Get-Process $env:ALLUSERSPROFILE | Where-Object -FilterScript {$_.Name -ne "SYSTEM, NETWORK SERVICE, LOCAL SERVICE"} | Where-Object -filterscript {$_.Name -ne "explorer"} | Stop-Process -WhatIf }
Cannot find a process with the name "C:\ProgramData". Verify the process name and call the cmdlet again.
+ CategoryInfo : ObjectNotFound: (C:\ProgramData:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : localhost
Here, this should work for you.
Function Stop-UserProcesses{
Param([string]$Computer = "localhost")
$Cred = Get-Credential
Invoke-Command -ComputerName $Computer -Credential $Cred -ScriptBlock {
Get-Process -IncludeUserName | Where{!($_.UserName -match "NT AUTHORITY\\(?:SYSTEM|(?:LOCAL|NETWORK) SERVICE)") -and !($_.ProcessName -eq "explorer")}|Stop-Process -WhatIf
}
}
Once you are convinced that it is functional remove the -WhatIf. Then just call it as Stop-UserProcesses to end everything locally, or Stop-UserProcesses SomeComputer01 to end everything on a remote system (assuming you have remote sessions enabled in your environment).
Edit: Well then, evidently the -IncludeUserName switch is new in v4. So, in order to do what you want we have to jump through hoops and use Get-WMIObject on the win32_process class, then execute the GetOwner() method for each process. Probably want to filter it so we don't end up with things like Idle throwing errors when they don't have an owner, so we'll make sure that the CommandLine property exists.
Function Stop-UserProcesses{
Param([string]$Computer = "localhost")
$Cred = Get-Credential
Invoke-Command -ComputerName $Computer -Credential $Cred -ScriptBlock {
#Get all processes
$Processes = get-wmiobject win32_process|Where{![string]::IsNullOrEmpty($_.commandline)}|Select *,#{l='Owner';e={$_.getowner().user}}
#Filter out System and service processes
$Processes = $Processes | Where { !($_.Owner -match "(?:SYSTEM|(?:LOCAL|NETWORK) SERVICE)") }
#Get processes and filter on the Process ID and name = explorer, then pipe to stop-process
Get-Process | Where { $Processes.ProcessID -contains $_.id -and $_.name -ne "explorer" } | Stop-Process -WhatIf
}
}

Powershell - Check on Remote Process, if done continue

As part of a backup operation, I am running the 7zip command to compress a folder into a single .7z file. No problems there as I am using the InVoke-WMIMethod.
Example:
$zip = "cmd /c $irFolder\7za.exe a $somedirectory.7z $somedirectory"
"InVoke-WmiMethod -class Win32_process -name Create -ArgumentList $zip -ComputerName $remotehost"
My problem comes in as my script continues, the 7za.exe process hasn't completed. I am then attempting to copy the item off of the remote system and it is either incomplete or fails.
Can someone point me in the direction to figure out how to identify if the 7za.exe process is still running, wait until it is dead, then proceed with the rest of my script?
I can grasp pulling the process from the remote system via...
get-wmiobject -class Win32_Process -ComputerName $remotehost | Where-Object $_.ProcessName -eq "7za.exe"}
Not sure how to turn that into usable info for my issue.
Answer UPDATE: (thx to nudge by #dugas)
This will do it with some feedback for those that need it...
do {(Write-Host "Waiting..."),(Start-Sleep -Seconds 5)}
until ((Get-WMIobject -Class Win32_process -Filter "Name='7za.exe'" -ComputerName $target | where {$_.Name -eq "7za.exe"}).ProcessID -eq $null)
You can invoke the Wait-Process cmdlet on the remote computer with the Invoke-Command cmdlet. Example:
$process = Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList notepad -ComputerName RemoteComputer
Invoke-Command -ComputerName RemoteComputer -ScriptBlock { param($processId) Wait-Process -ProcessId $processId } -ArgumentList $process.ProcessId
Since you mentioned using Invoke-Command is not an option, another option is polling.
Example:
$process = Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList notepad -ComputerName hgodasvccr01
$processId = $process.ProcessId
$runningCheck = { Get-WmiObject -Class Win32_Process -Filter "ProcessId='$processId'" -ComputerName hgodasvccr01 -ErrorAction SilentlyContinue | ? { ($_.ProcessName -eq 'notepad.exe') } }
while ($null -ne (& $runningCheck))
{
Start-Sleep -m 250
}
Write-Host "Process: $processId is not longer running"
You should be able to do it with a do... while loop that just sleeps until the process is finished.
do {
"waiting"
start-sleep 10
} while (gwmi -class win32_process -ComputerName $remotehost | Where ProcessName -eq "7za.exe")