Processing servers in parallel - parallel-processing

ServiceStatus -Servers "Machine1", "machine2", "machine3"
workflow ServiceStatus
{
param([string[]]$servers)
foreach -Parallel ($server in $servers)
{
Write-Host "Chekcing status in $server..." -ForegroundColor black -BackgroundColor green
Get-Service W3SVC -ComputerName $server | Format-Table Name,Status,MachineName -AutoSize
if(!$?)
{
Out-File $FileName1 -Append -InputObject $server -Encoding ascii
}
}
}
I try to make foreach to execute in parallel, but it is giving me a "syntax error near foreach loop" error.

Format-* cmdlets can only be used inside InlineScript sections in PowerShell workflows. See here for more information. Same goes for Write-Host.
Also, you must define your workflow before you invoke it.
workflow ServiceStatus {
...
}
ServiceStatus -Servers "Machine1", "machine2", "machine3"

Related

Powershell - Check if one or both Services are running

I need to check for running Services on different Windows Servers.
there can be two services running (old or new version or both, with slightly different names)
need the output like: "new version" , "old version" or "both versions"
I wanted to simply check with the Get-Process command but I can't really get to a conclusion how to get my output and how to check if both are running.
Started like the following: how can I finish my script? or am I completely wrong? couldn't find anything that helps.
if (Get-Service "NAME" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{Write-Host "New Client running"
}
if (Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{Write-Host "old Client running"
}
Else {Write-Host ”No Client found”}
obviously this script doesn't quite work. tested on a Server where only the new client is running and it outputs:
New Client running
No Client found
Change the second if statement to elseif - this way the else block only runs if neither of the two preceding conditions hold true:
if (Get-Service "NAME" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{
Write-Host "New Client running"
}
elseif (Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where-Object {$_.Status -eq "Running"})
{
Write-Host "old Client running"
}
else {
Write-Host "No Client found"
}
You can do this with your three if statements if you track your Get-Service results:
if ($newclient = Get-Service "NAME" -ErrorAction SilentlyContinue | Where Status -eq Running) {
Write-Host 'New Client running'
}
if ($oldclient = Get-Service "NAME_old" -ErrorAction SilentlyContinue | Where Status -eq Running) {
Write-Host 'old Client running'
}
if (-not ($newclient -or $oldclient)) {
Write-Host 'No Client found'
}
Slightly different approach :
"name1", "name2" | Get-Service | foreach-object {
write-host $_.name $_.status
}
Of course depends on logic required after
if you are interested in a little more scalable solution, I would suggest following:
I used $appsRunning to increment each time the script find a running service, so if you are interested in a total count of running services (from the $services list) you could use that variable.
$services = #(
[pscustomobject]#{ Name = "NAME"; Description = "New App" },
[pscustomobject]#{ Name = "NAME_old"; Description = "Old App" }
)
$appsRunning = 0
foreach ($service in $services) {
$app = Get-Service $service.Name
if ($app.status -eq "Running"){
write-host $service.Description is $app.status
$appsRunning++
}
}
if ($appsRunning -eq 0) {
Write-Host "No app Running"

How can I use this code on remote computer

I'm trying to use this function to monitor BSOD remotely I know I have to add a parameter of computername just not sure where
Function Get-BSODInfo{
try {
Invoke-WebRequest -Uri "https://www.nirsoft.net/utils/bluescreenview.zip" -OutFile "$($ENV:Temp)\bluescreeview.zip"
Expand-Archive "$($ENV:Temp)\bluescreeview.zip" -DestinationPath "$($ENV:Temp)" -Force
Start-Process -FilePath "$($ENV:Temp)\Bluescreenview.exe" -ArgumentList "/scomma `"$($ENV:Temp)\Export.csv`"" -Wait
}
catch {
Write-Host "BSODView Command has Failed: $($_.Exception.Message)"
exit 1
}
$BSODs = get-content "$($ENV:Temp)\Export.csv" | ConvertFrom-Csv -Delimiter ',' -Header Dumpfile, Timestamp, Reason, Errorcode, Parameter1, Parameter2, Parameter3, Parameter4, CausedByDriver | foreach-object { $_.Timestamp = [datetime]::Parse($_.timestamp, [System.Globalization.CultureInfo]::CurrentCulture); $_ }
Remove-item "$($ENV:Temp)\Export.csv" -Force
$BSODFilter = $BSODs | where-object { $_.Timestamp -gt ((get-date).addhours(-24)) }
if (!$BSODFilter) {
write-host "Healthy - No BSODs found in the last 24 hours"
}
else {
write-host "Unhealthy - BSOD found. Check Diagnostics"
$BSODFilter
exit 1
}
}

IIIS WAS process cannot be stopped via Powershell

On a Windows Server 2008 R2, 64 bit-machine I am running the following code:
$global:arrServer = #("ph1", "ph2", "ph3")
$global:arrDienste = #("W3SVC", "WAS", "IISADMIN")
$global:strPfad = "D:\WASLogs\"
$global:strLogTime = Get-Date -Format "yyyy-MM-dd--hh-mm-ss"
$global:strLogDatei = $global:strPfad + "WARTUNG--" + $global:strLogTime + ".log"
Log_Abfrage_und_Generierung
Dienste_Stop
Function Dienste_Stop
{
echo "Stop of the services successful?" | Out-File $global:strLogDatei -Append -Force
foreach($strServer in $global:arrServer)
{
$strInterim2 = $strServer + " (" + $global:appServerNamen + ")"
echo " " $strInterim2 | Out-File $global:strLogDatei -Append -Force
foreach($strDienst in $global:arrDienste)
{
$objWmiService = Get-Wmiobject -Class "win32_service" -computer $strServer -filter "name = '$strDienst'"
if( $objWmiService.State )
{
$rtnWert = $objWmiService.stopService()
Switch ($rtnWert.returnvalue)
{
0 { echo "$strDienst stopped!" | Out-File $global:strLogDatei -Append -Force }
2 { echo "$strDienst throws: 'Access denied!'" | Out-File $global:strLogDatei -Append -Force }
3 { echo "Service $strDienst is not existing on $strServer!" | Out-File $global:strLogDatei -Append -Force }
5 { echo "$strDienst already stopped!" | Out-File $global:strLogDatei -Append -Force }
DEFAULT { echo "$strDienst service reports ERROR $($rtnWert.returnValue)" | Out-File $global:strLogDatei -Append -Force }
}
}
else
{
echo "Service $strDienst is not existing on $strServer!" | Out-File $global:strLogDatei -Append -Force
}
}
}
}
Function Log_Abfrage_und_Generierung
{
if([IO.Directory]::Exists($global:strPfad))
{
echo "Nothing happening here."
}
else
{
New-Item -ItemType directory -path $global:strPfad
}
}
This can be reproduced on all computers ph1, ph2 and ph3. However with some other code, WAS can be started, respectively the status can be seen.
Also to note:
All other services can be stopped? Does it has to do with the fact that the path for the WAS is like this? C:\Windows\system32\svchost.exe -k iissvcs
I use WMI on purpose.
What is going on here?
Tia
The problem could be that there are multiple services that depend on WAS which need to be stopped first. The StopService() method does not have an overload to stop dependent services. If this doesn't solve the issue check the response code from StopService to determine the problem in the link above.
It looks like you are handling the code 3 as 'service does not exist'. The docs show this code actually means 'The service cannot be stopped because other services that are running are dependent on it.'
Not sure why you're determined to use WMI when this capability is fully baked into powershell
Stop-Service WAS -Force

'Session' Parameter is null or empty in PowerShell script

I'm very new to PowerShell (forgive my ignorance) and am trying to install a program remotely on multiple computers on my domain. Currently, I'm just trying to get it to work on one computer. The script below was found online and adapted for my needs. Yesterday it worked, but today it's complaining about the session parameter.
I don't fully understand "sessions", but I have ensured on the client machine that the winrm service is running and I have invoked Enable-PSRemoting -force.
Here's the script:
$computers = Get-Content "c:\tmpPS\computers.txt"
$rs = Get-PSSession
Get-PSSession | Get-Member
######
## Functions
################
foreach ($comp in $computers)
{
Write-Host "should work with $comp"
}
PushMSI
RemoteConnect
InstallMSI
Function PushMSI {
Write-Host "------------------------------------------------"
Write-Host "This will copy the MSI file from localhost c:\tmpPS\"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
foreach ($comp in $computers)
{
Copy-Item -path "c:\tmpPS\clientInstall.msi" -Destination \\$comp\c$\tmpPS
}
}
Function RemoteConnect
{
Write-Host "------------------------------------------------"
Write-Host "This will establish a PSSession with all computers in c:\temp\computers.txt"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
Get-Content C:\tmpPS\computers.txt | New-PSSession -ThrottleLimit 50
}
Function InstallMSI
{
Write-Host "------------------------------------------------"
Write-Host "This will Install UPS Update on all computers with an Established PSSession"
write-Host "------------------------------------------------"
Write-Host "After the Install PSSessions will be removed"
Write-Host ""
Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.msi"}
}
Get-PSSession | Remove-PSSession
And here's the output:
PS C:\Users\Me> C:\tmpPS\remoteInstall.ps1
Get-Member : No object has been specified to the get-member cmdlet.
At C:\tmpPS\remoteInstall.ps1:3 char:17
+ Get-PSSession | Get-Member
+ ~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
should work with eSignWin81.informa.local
------------------------------------------------
This will copy the MSI file from localhost c:\tmpPS\
------------------------------------------------
------------------------------------------------
This will establish a PSSession with all computers in c:\temp\computers.txt
------------------------------------------------
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
6 Session6 eSignWin81.i... Opened Microsoft.PowerShell Available
------------------------------------------------
This will Install UPS Update on all computers with an Established PSSession
------------------------------------------------
After the Install PSSessions will be removed
Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Supply an argument that is not null or empty and then try
the command again.
At C:\tmpPS\remoteInstall.ps1:49 char:25
+ Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.ms ...
+ ~~~
+ CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
Try not to use Write-Host as it brakes the pipeline and kills puppy's.
I've tried to improve your script a little bit, so you understand the logic behind the structure better. It hasn't been tested, but it should do the trick.
# First parameters
[CmdletBinding()]
Param(
[ValidateScript({Test-Path $_ -PathType leaf})]
$ComputerList = "c:\tmpPS\computers.txt",
[ValidateScript({Test-Path $_ -PathType leaf})]
$MSI = "c:\tmpPS\clientInstall.msi"
)
# Then functions
Begin {
Function Copy-MSI {
foreach ($Com in $Computers) {
Copy-Item -path $MSI -Destination "\\$Com\c$\tmpPS"
}
}
Function Install-MSI {
foreach ($Com in $Computers) {
Enter-PSSession -ComputerName $Com
invoke-item "c:\tmpPS\ClientInstall.msi"
Exit-PSSession
}
}
}
# Then the actions
Process {
$Computers = Get-Content $ComputerList
Copy-MSI
Install-MSI
}
If you run this, you'll find the information you're looking for:
Get-Help Enter-PSSession

If Then Else broke my script

Currently I have this code -
Set-ExecutionPolicy Unrestricted
$name = (Get-WmiObject win32_bios).SerialNumber.Trim()
$oldname = (Get-WmiObject win32_computersystem).Name.Trim()
IF ($oldname -eq $name){Exit}
Else{ Rename-computer -ComputerName $oldname -NewName "$name" -force
Start-Sleep -s 5
Restart-Computer}
And I have it set to run as a scheduled task at logon and without the If Else it works perfectly however I don't want it to run every time a user logs in because it will just be a cycle of rebooting. Any help would be greatly appreciated.
I would suggest some changes:
Set-ExecutionPolicy is unnecessary, because if the machine has started processing the script, then the executionpolicy isn't a problem. So remove that, and specify it in the powershell.exe-call instead, like: powershell.exe -executionpolicy unrestricted
Use if($oldname -ne $name) { rename-computer .... } so you can remove the else part. Much cleaner
Try running the modified script below, and report back with the output in the scriptlog.txt-file.
$logpath = "c:\scriptlog.txt"
$name = (Get-WmiObject win32_bios).SerialNumber.Trim()
$oldname = (Get-WmiObject win32_computersystem).Name.Trim()
"NewName is '$name'" | Out-File $logpath -Append
"OldName is '$oldname'" | Out-File $logpath -Append
IF ($oldname -ne $name){
"If-test TRUE" | Out-File $logpath -Append
Rename-computer -ComputerName $oldname -NewName $name -Force
Start-Sleep -s 5
Restart-Computer
} else { #I've added the else-part just because of logging.
"IF-test FALSE" | Out-File $logpath -Append
}

Resources