Powershell Remote Stop and Disable Service - powershell

SO Braintrust. I'm not a Powershell person, but I'm working on it. Trying to address yet another zero-day, I'm trying to build a reuseable script to remotely stop and disable the affected service. It is based on a script I got from a Microsoft MVP at (ultimately): http://portal.sivarajan.com/2010/07/stopstart-or-enabledisable-service_26.html
The prompt for the service name was added by me as well as the output information (Write-host & Add-Content lines), so I could get a results summation (the output part's not working fully, but it's the least of my concerns at the moment.).
$output = "c:\scripts\results.csv"
Add-content -path $output "======================"
Add-content -path $output "StopAndDisableService Output Start"
cls
$Cred = Get-Credential
$service = Read-Host -Prompt 'Enter Service Name" '
Import-CSV C:\Scripts\computers.csv | %
{
$computer = $_.ComputerName
Write-Host "Working on $computer"
Add-content -path $output "$computer"
$result = (Get-WmiObject win32_service -computername $computer -filter "name='$service'" -Credential $cred).stopservice()
Add-content -path $output " Stop - $result"
$result = (Get-WmiObject win32_service -computername $computer -filter "name='$service'" -Credential $cred).ChangeStartMode("Disabled")
Add-content -path $output " Disable - $result"
}
Add-content -path $output "======================"
Add-content -path $output "StopAndDisableService Output End"
when I run it, I get an error on the computer name
Get-WmiObject : Cannot validate argument on parameter 'ComputerName'.
The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Scripts\StopAndDisableService.ps1:12 char:54
+ ... result = (Get-WmiObject win32_service -computername $computer -filter ...
+ ~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Get-WmiObject : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Scripts\StopAndDisableService.ps1:14 char:54
+ ... result = (Get-WmiObject win32_service -computername $computer -filter ...
+ ~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Computer.csv contains one computer name per line, no punctuation, no FQDN, just the computer name

Special thanks to #Mathias R. Jessen for his help on this. Final working code. you will have to analyze the screen output to catch any errors and see which machines it did not catch due to being offline # time of running (some output file items have been commented out since they don't work as intended)
$output = "c:\scripts\results.csv"
Add-content -path $output "======================"
Add-content -path $output "StopAndDisableService Output Start"
cls
$Cred = Get-Credential
$service = Read-Host -Prompt 'Enter Service Name" '
Import-CSV C:\Scripts\computers.csv -Header ComputerName | % {
$computer = $_.ComputerName
Write-Host "Working on $computer"
Add-content -path $output "$computer"
$result = (Get-WmiObject win32_service -computername $computer -filter "name='$service'" -Credential $cred).stopservice()
#Add-content -path $output " Stop - $result"
$result = (Get-WmiObject win32_service -computername $computer -filter "name='$service'" -Credential $cred).ChangeStartMode("Disabled")
#Add-content -path $output " Disable - $result"
}
Add-content -path $output "======================"
Add-content -path $output "StopAndDisableService Output End"
Analyzing results on the screen output, any results with
Just the machine name - means it's processed without error on that machine (success)
RPC server is unavailable means machine is offline
Cannot call a method on Null-Valued expression on line 12 or line 14 means that service doesn't exist on that machine
The results.csv output file will contain list of names of the machines this script was run against

Related

run powershell command over cmd

I'm trying to run this powershell command over cmd.. it worked when i run it directly from powershell.. but when i try to run if from cmd i get errors
Powershell Command:
(Get-WmiObject -Class Win32_Product -Filter "Name='Symantec Endpoint Protection'" -ComputerName localhost. ).Uninstall()
How I run it (cmd):
powershell.exe -Command (Get-WmiObject -Class Win32_Product -Filter Name='Symantec Endpoint Protection' -ComputerName localhost. ).Uninstall()
Output:
Get-WmiObject : Invalid query "select * from Win32_Product where Name=Symantec
Endpoint Protection"
At line:1 char:2
+ (Get-WmiObject -Class Win32_Product -Filter Name='Symantec Endpoint P ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], Management
Exception
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.C
ommands.GetWmiObjectCommand
You cannot call a method on a null-valued expression.
At line:1 char:1
+ (Get-WmiObject -Class Win32_Product -Filter Name='Symantec Endpoint P ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
The other answers already answer your question of running powershell over CMD. I'd like to recommend you stop using the Win32_Product wmi class. You can read any of the never ending articles explaining why. As for building commands with arguments, I recommend splatting. As a bonus specifically regarding removing SEP, here is a snippet from a production script used to remove Symantec Endpoint using MSIexec and the guid.
$DateStamp = get-date -Format yyyyMMddTHHmmss
$logFile = '{0}-{1}-{2}.log' -f 'SymantecUninstall',$PC,$DateStamp
$locallog = join-path 'c:\windows\temp' -ChildPath $logFile
$uninstalljobs = Foreach($PC in $SomeList){
start-job -name $pc -ScriptBlock {
Param($PC,$locallog)
$script = {
Param($locallog)
$MSIArguments = #(
"/x"
('"{0}"' -f '{A0CFB412-0C01-4D2E-BAC9-3610AD36B4C8}')
"/qn"
"/norestart"
"/L*v"
$locallog
)
Start-Process "msiexec.exe" -ArgumentList $MSIArguments -Wait -NoNewWindow
}
Invoke-Command -ComputerName $pc -ArgumentList $locallog -ScriptBlock $script
} -ArgumentList $PC,$locallog
}
Just update the guid to match your product. If you want to pull the uninstall string from the registry and use that, it would also be preferable to Win32_Product.
Here are a couple of ways you can find the uninstallstring.
$script = {
$ErrorActionPreference = 'stop'
"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach{
try
{
$key = reg query $_ /f "Symantec Endpoint" /s | select -skip 1 -first 1
$key = $key -replace 'HKEY_LOCAL_MACHINE','HKLM:'
(Get-ItemProperty $key -Name UninstallString).UninstallString
}
catch{}
}
}
powershell.exe -command $script
or
$script = {
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach{
Get-childitem $_ |
Where {($_ | get-itemproperty -Name displayname -ea 0).displayname -like 'Symantec Endpoint*'} |
Get-ItemPropertyValue -name UninstallString
}
}
powershell.exe -command $script
Try this:
powershell.exe -Command "& {(Get-WmiObject -Class Win32_Product -Filter """Name='Symantec Endpoint Protection'""" -ComputerName XOS-MS182. ).Uninstall()}"
Try these. The parentheses mean something special to cmd. The filter would require two sets of quotes. Since the pipe is inside the double quotes, cmd ignores it.
powershell "(Get-WmiObject -Class Win32_Product -ComputerName localhost | where name -eq 'symantec endpoint protection').Uninstall()"
powershell "Get-WmiObject win32_product -cn localhost | ? name -eq 'symantec endpoint protection' | remove-wmiobject"
You don't need to use powershell for this task, from an elevated Windows Command Prompt, (cmd), you could use wmic instead:
WMIC.exe Product Where "Name='Symantec Endpoint Protection'" Call Uninstall

How to create Powershell custom error output?

I want to make a small PS script that checks the status of a service logon account against a server list.
What i need to do is, if a server is down, it shows a custom error message that tells me which server from the list is offline, instead of the default bulky red error message.
Here is what i came up with so far.
This is the RPC error powershell shows if something wrong with a server.
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At V:\HRG01\MPE_HRG01_Information-Technology\Share\ITSS-Core\Icinga Monitor Software\Service Check\Get-Service Log On.ps1:1 char:1
+ Get-WmiObject win32_service -ComputerName (Get-Content -path ".\serverlist.txt") ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
PS Exception
PS c:\> $Error[0].Exception.GetType().FullName
System.Runtime.InteropServices.COMException
I searched on the internet for a solution, and this is what i came up with at last, but of course not working.
$servers= Get-Content -path ".\serverlist.txt"
foreach ($server in $servers)
{
try {
Get-WmiObject win32_service -ComputerName $server |
Where-Object {$_.Name -eq "icinga2"} -ErrorAction Continue |
format-list -Property PSComputerName,Name,StartName
}
catch [System.Runtime.InteropServices.COMException]
{
Write-Host "ERROR: $Server connection error"
}
}
Tee-Object .\Results.txt -Append
Read-Host -Prompt "Press Enter to exit"
I'd really appreciate your help
The error is on Get-WmiObject not Where-Object. And you have to set error action to stop to catch terminating error.
$servers= Get-Content -path ".\serverlist.txt"
foreach ($server in $servers)
{
try {
Get-WmiObject win32_service -ComputerName $server -ErrorAction Stop |
Where-Object {$_.Name -eq "icinga2"} |
format-list -Property PSComputerName,Name,StartName
}
catch [System.Runtime.InteropServices.COMException]
{
Write-Host "ERROR: $Server connection error"
}
}
Tee-Object .\Results.txt -Append
Read-Host -Prompt "Press Enter to exit"

Powershell Script reading file into array

I have a script I'm working on. I want it to read in a column named ComputerName and one named UserName.
My CSV file looks like this:
ComputerName | Username
computer01 | user1
computer02 | user2
The Pipes are representing cells in excel.
Here's my script:
$computerName = #()
$userName = #()
Import-Csv C:\test\script\Computername_username_test.csv -Delimiter "|" |`
ForEach-Object
{
$computerName += $_.ComputerName
$userName += $_.UserName
}
$destination = New-Item -ItemType Directory -Path C:\test\$userName\dictionary_Files\ -force
$fileList = Get-WmiObject -Class CIM_DataFile -Filter "Drive='C:' And Extension='dic'" -Computername $computerName
foreach ($file in $fileList)
{
$drive, $path = $file.Name.Split('\',2)
$drive = $drive -replace ':','$'
$remoteFile = "\\$computerName\$drive\$path"
Write-Verbose "Copy $remoteFile to $destination"
Copy-Item $remoteFile -Destination $destination -Confirm
}
My goal is to search the C drive of the remote computer for all files with the .dic extension and copy them to a location inside a folder that is named the same as their username from the excel sheet.
When I run this I'm getting the following:
PS C:\Test\Script> C:\Test\Script\DicFiles03_importCSV.ps1
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]:
$computerName += $_.ComputerName
$userName += $_.UserName
Get-WmiObject : Cannot validate argument on parameter 'ComputerName'. The argument is null, empty, or an element of the argument
collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
At C:\Test\Script\DicFiles03_importCSV.ps1:13 char:102
+ ... -Filter "Drive='C:' And Extension='dic'" -Computername $computerName
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Thank you for your help.
I'm think its because you have your { after the foreach-object on the next line powershell is a scripting language so its particular about line endings.

How to remotely rename a list of computers via PowerShell?

I have a largish set of Windows 10 workstations that need to be renamed. I've tried running the script below, but get errors that are beyond my current PS level.
$computers = Import-Csv "c:\rename-computers\computers.csv"
foreach ($oldname in $computers){
#Write-Host "EmpID=" + $computers.NewName
Rename-Computer -ComputerName $computers.OldName -NewName $computers.NewName -DomainCredential hole\inwall -Force -Restart
}
Produces:
Rename-Computer : Cannot convert 'System.Object[]' to the type
'System.String' required by parameter 'ComputerName'. Specified
method is not supported. At
\siat-ds0\appdeploy\LabPacks\rename-computers\rename-siat.ps1:4
char:35
+ Rename-Computer -ComputerName $computers.OldName -NewName $computers.NewName ...
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Computer], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.RenameComputerCommand
I've seen similar closed threads on this topic elsewhere without mention of the error I'm receiving.
You mistakenly used the collection variable $computers instead of the loop-iteration variable $oldname inside your loop, and since $computers.NewName expanded to an array of names rather than a single one, you got the error you saw.
That said, you don't need a loop at all - a single pipeline will do:
Import-Csv "c:\rename-computers\computers.csv" |
Rename-Computer -ComputerName { $_.OldName } -DomainCredential hole\inwall -Force -Restart
Rename-Computer will implicitly bind the NewName property of each input object to the -NewName parameter.
The -ComputerName parameter, by contrast, must be told what property on the input objects to access, given that the input objects have no ComputerName property.
This is what script block { $_.OldName } does, inside which automatic variable $_ represents the input object at hand.
To see which parameters accept pipeline input, examine the output from
Get-Help -full Rename-Computer; for details and a programmatic alternative, see this answer of mine.
You are iterating but not using the singular:
Instead of this:
foreach ($oldname in $computers){
#Write-Host "EmpID=" + $computers.NewName
Rename-Computer -ComputerName $computers.OldName -NewName $computers.NewName -DomainCredential hole\inwall -Force -Restart
}
Try this:
foreach ($oldname in $computers){
Rename-Computer -ComputerName $oldname.OldName -NewName $oldname.NewName -DomainCredential hole\inwall -Force -Restart
}
Note: $oldname is holding one value at a point. So the number of computers present in $computers will come one by one to $oldname and will perform the activity inside the loop.
You should use the singular $oldname inside the loop to iterate one by one.
Bulk rename computers in AD
Powershell bulk rename computers in AD with test if pc is online and if new name is already taken and log "not-renamed" PC.
adc.csv
oldname,newname
WEDSKS0022,RKVKS0110
WEDSKS0117,RKVKS1413
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -Force;
$computers = import-csv -Path ".\adc.csv"
$Credential = Get-Credential
$nisuprosli=".\notrenamed $(get-date -f dd-MM-yyyy-HHMM).csv"
$makecsv="oldname,newname" | Out-File $nisuprosli -Encoding utf8 -Append
foreach ($pc in $computers){
$IsOldPCalive=Test-Connection -ComputerName $pc.oldname -Quiet -Count 1 -ErrorAction SilentlyContinue
$IsNewPCalive=Test-Connection -ComputerName $pc.newname -Quiet -Count 1 -ErrorAction SilentlyContinue
if ($IsOldPCalive -eq $True -and $IsNewPCalive -eq $False) {
write-host "Rename PC $($pc.oldname) u $($pc.newname)" -ForegroundColor Cyan
Rename-computer -computername $pc.oldname -newname $pc.newname -domaincredential $Credential -PassThru -force -restart #-WhatIf
}
else {
write-host "PC $($pc.oldname) is not available or already exists $($pc.newname)" -ForegroundColor Yellow
$makecsv="$($pc.oldname),$($pc.newname)" | Out-File $nisuprosli -Encoding utf8 -Append
}
}

Invoke-Command Cannot Bind Argument Error

Is Invoke-Command not pulling my variables?
Im caught on this one. I could use an xtra set of eyes!
Im pulling services from a remote machine and assigning them by number then passing a stop / start to the remote machine based off user input. Im getting a argument on my variable.
Please forgive the code setup Im new and I write then I clean. Some services and names have been removed for privacy.
Code::
$prepend = "ssssssssss"
$append = "sss"
$Fprepend = "tttttttt"
$Fappend = "tt"
$sitenumber = Read-Host 'What is the site number? ex. 1111'
$name = $prepend + $sitenumber + $append
$Fname = $Fname = $Fprepend + $sitenumber + $Fappend
$global:i=0
Get-service -Name Service,Instance,Server,Integration,Data,Message,FTP,Provider -ComputerName $name |
Select #{Name="Item";Expression={$global:i++;$global:i}},Name -OutVariable menu | Format-Table -AutoSize
$r = Read-Host "Select a service to restart by number"
$svc = $menu | where {$_.item -eq $r}
Write-Host "Restarting $($svc.name)" -ForegroundColor Green
Invoke-Command -ComputerName $Fname -ScriptBlock {Stop-Service -Name $svc.name -Force}
sleep 3
Invoke-Command -ComputerName $Fname -ScriptBlock {Start-Service -Name $svc.name -Force}
Get-service -Name $svc.name -Computername $name
Error::
Cannot bind argument to parameter 'Name' because it is null.
+ CategoryInfo : InvalidData: (:) [Stop-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.StopServiceCommand
Cannot bind argument to parameter 'Name' because it is null.
+ CategoryInfo : InvalidData: (:) [Start-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.StartServiceCommand
I have modified the code and now its working fine.
The issue what you are facing is because inside the script block , $svc is not holding any value because its not in the scope even. To make it within the scope you have to pass as an ArgumentList and it has to be initiated inside the block as param. Thats why ou are getting as Null
Use the below code. I just modified the Invoke part
$prepend = "ssssssssss"
$append = "sss"
$Fprepend = "tttttttt"
$Fappend = "tt"
$sitenumber = Read-Host 'What is the site number? ex. 1111'
$name = $prepend + $sitenumber + $append
$Fname = $Fname = $Fprepend + $sitenumber + $Fappend
$global:i=0
Get-service -Name Service,Instance,Server,Integration,Data,Message,FTP,Provider -ComputerName $name |
Select #{Name="Item";Expression={$global:i++;$global:i}},Name -OutVariable menu | Format-Table -AutoSize
$r = Read-Host "Select a service to restart by number"
$svc = $menu | where {$_.item -eq $r}
Write-Host "Restarting $($svc.name)" -ForegroundColor Green
# You have to pass the $svc as an argumentlist and the same has to be initiated as param inside the script block.
Invoke-Command -ComputerName $Fname -ScriptBlock {param($svc)Stop-Service -Name $svc.name -Force} -ArgumentList $svc
sleep 3
Invoke-Command -ComputerName $Fname -ScriptBlock {param($svc)Start-Service -Name $svc.name -Force} -ArgumentList $svc
Get-service -Name $svc.name -Computername $name
Hope you understand the issue now.