PowerShell script wont scan for processes - powershell

I'm new to scripting any I am trying to make a script that reads PC list from a CSV file and check if a specific process is runnuing.
the following code is:
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\to\log.txt" # Location of local log file name
$CsvLocation = "\\path\to\CSV\File\Table.csv" #Location of the CSV file
$csvdata = Import-Csv $ipmCsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp.exe"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
$Processes = get-process | Where-Object {$_.ProcessName -Like "nscp*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -eq $IcingaProc){
Write-Host "Program installed succefully"
LogWrite ("Program installed succefully in "+$pcname)
LogWrite `r`n
}
else{
LogWrite ("Could not find any CINIGA process")
LogWrite `r`n
}
}
}
This is the output I get:
PS User> powershell.exe -ExecutionPolicy Bypass –Noprofile -file "Path\to\script.ps1"
Looking for process nscp.exe in PC1...
Looking for process nscp.exe in PC2...
Looking for process nscp.exe in PC3...
Looking for process nscp.exe in PC4...
it doesn't seems to enter the 2nd foreach loop..

I was indeed pointing to localhost and changed to point to $pcName.
When i "turned" the if command with -ne instead of -eq it worked well!
Thank you for all your help !
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\To\LogFIle.txt" # Location of local log file name
$CsvLocation = "\\Path\To\Referance\CSV\Table.csv"
$csvdata = Import-Csv $CsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
if (Test-Connection -ComputerName $pcname -Count 1 -Quiet) {
$Processes = get-process -ComputerName $pcname | Where-Object {$_.ProcessName -Like "ns*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -ne $IcingaProc){
Write-Host ("Could not find any process")
}
else{
Write-Host "Program is installed succefully"
LogWrite ("Program is installed on "+$pcname)
}
}
}
else{
Write-Host ("The PC is not reachable!")
}
}

Related

get-process - Unable to retrieve some process information and command line

I've 2 questions regarding the code below :
Question 1:
For some unknown reason, the following code does not retrieve information from certain processes.
However, he manages to recover the sha256
Could you please explain why this behavior and how to fix address this behavior ?
Path is Null
WmiPrvSE
- [(PID=5356) () Description=]
-- Path="None (SHA256=AD938C303F12EA8D164433CC7BA46FC7B9AE00F6F899E308D4317DAB46E25642)
-----
Get-FileHash : Impossible de lier l'argument au paramètre « Path », car il a la valeur Null.
Au caractère C:\Users\LEFBE\Desktop\Klic-20-12-2021\Process.ps1:4 : 33
+ $FileHash = (Get-FileHash -Path $_.Path -Algorithm SHA256 -ErrorActio ...
+ ~~~~~~~
+ CategoryInfo : InvalidData : (:) [Get-FileHash], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-FileHash
Question 2:
I'm having trouble retrieving the command line launched by the process. Could you please help me to get this information?
The code :
get-process | ForEach-Object {
$Name = $_.Name
$Desc = $_.Description
$FileHash = (Get-FileHash -Path $_.Path -Algorithm SHA256 -ErrorAction SilentlyContinue).hash
$ID = $_.ID
$Path = $_.Path
$CP = $_.Company
$CL = $_.Commandline
if ($Path -eq $null)
{
Write-Host "Path is Null"
$Path = "None"
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
else
{
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
}
Update 23/12/2021
The updated code works better than the first. (manage Null $Path value)
On the other hand, no command line can be obtained yet.
get-process | ForEach-Object {
$Name = $_.Name
$Desc = $_.Description
$ID = $_.ID
$Path = $_.Path
$CP = $_.Company
$CL = $_.Commandline
IF([string]::IsNullOrEmpty($Path)) {
$Path = "None"
$FileHash = "None"
write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
} else {
$FileHash = (Get-FileHash -LiteralPath $_.Path -Algorithm SHA256 -ErrorAction SilentlyContinue).hash
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
}
Thanks for your help,
LEFBE
You can use a calculated property with Select-Object to generate your new object. As for "having trouble retrieving the command line", I'm assuming you're unable to capture the output of your script with Out-File or similar, this is because Write-Host sends the output to the Information Stream and it's output can't be captured unless redirected (6>&1).
Get-Process | Select-Object Name, Description, #{
Name = 'Hash'
Expression = { (Get-FileHash $_.Path -Algorithm SHA256).Hash }
}, ID, Path, Company, CommandLine

How to execute command on remote server using Invoke-Command as Domain Administrator?

I am trying to modify a script that used to work when executed on each of the servers manually to generate the DFS replication then send an email, so it can be executed once from one location then remotely connect to each and every server.
Invoke-Command:
$ComputerName = Get-DfsrMember | Select-Object -ExpandProperty ComputerName -Unique | Sort-Object
$ComputerName | ForEach-Object {
Try {
$session = New-PSSession -ComputerName $_ -ErrorAction Stop
Invoke-Command -ErrorAction Stop -Session $session -ScriptBlock {
## Script body starts here....
Write-Host "Processing on server $($_) `n" -ForegroundColor Yellow
Param (
[String[]]$ReplicationGroupList = ("")
)
....
}
}
Catch [System.UnauthorizedAccessException] {
Write-Warning "You do not have the proper access to this system!"
Break
}
Catch [System.Runtime.InteropServices.COMException] {
Write-Warning "Communications Exception occurred!"
Break
}
}
This is the error I have received:
The term 'Param' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:7 char:9
+ Invoke-Command -ErrorAction Stop -Session $session -ScriptBlo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Param:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : PRDDC01-VM
The actual Powershell script that used to be working, but must be manually executed in each of the server RDP session:
Param (
[String[]]$ReplicationGroupList = ("")
)
$RGroups = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query "SELECT * FROM DfsrReplicationGroupConfig"
#If replication groups specified, use only those.
if ($ReplicationGroupList) {
$SelectedRGroups = #()
foreach ($ReplicationGroup IN $ReplicationGroupList) {
$SelectedRGroups += $rgroups | Where-Object { $_.ReplicationGroupName -eq $ReplicationGroup }
}
if ($SelectedRGroups.count -eq 0) {
Write-Error "None of the group names specified were found, exiting"
exit
}
else {
$RGroups = $SelectedRGroups
}
}
$ComputerName = $ENV:ComputerName
$Succ = 0
$Warn = 0
$Err = 0
Start-Transcript -path "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
foreach ($Group in $RGroups) {
$RGFoldersWMIQ = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID='" + $Group.ReplicationGroupGUID + "'"
$RGFolders = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query $RGFoldersWMIQ
$RGConnectionsWMIQ = "SELECT * FROM DfsrConnectionConfig WHERE ReplicationGroupGUID='" + $Group.ReplicationGroupGUID + "'"
$RGConnections = Get-WmiObject -Namespace "root\MicrosoftDFS" -Query $RGConnectionsWMIQ
foreach ($Connection in $RGConnections) {
$ConnectionName = $Connection.PartnerName#.Trim()
if ($Connection.Enabled -eq $True) {
if (((New-Object System.Net.NetworkInformation.ping).send("$ConnectionName")).Status -eq "Success") {
foreach ($Folder in $RGFolders) {
$RGName = $Group.ReplicationGroupName
$RFName = $Folder.ReplicatedFolderName
if ($Connection.Inbound -eq $True) {
$SendingMember = $ConnectionName
$ReceivingMember = $ComputerName
$Direction = "inbound"
}
else {
$SendingMember = $ComputerName
$ReceivingMember = $ConnectionName
$Direction = "outbound"
}
$BLCommand = "dfsrdiag Backlog /RGName:'" + $RGName + "' /RFName:'" + $RFName + "' /SendingMember:" + $SendingMember + " /ReceivingMember:" + $ReceivingMember
$Backlog = Invoke-Expression -Command $BLCommand
$BackLogFilecount = 0
foreach ($item in $Backlog) {
if ($item -ilike "*Backlog File count*") {
$BacklogFileCount = [int]$Item.Split(":")[1].Trim()
}
}
if ($BacklogFileCount -eq 0) {
$Color = "white"
$Succ = $Succ + 1
}
elseif ($BacklogFilecount -lt 10) {
$Color = "yellow"
$Warn = $Warn + 1
}
else {
$Color = "red"
$Err = $Err + 1
}
Write-Output "$BacklogFileCount files in backlog $SendingMember->$ReceivingMember for $RGName"
} # Closing iterate through all folders
} # Closing If replies to ping
} # Closing If Connection enabled
} # Closing iteration through all connections
} # Closing iteration through all groups
Write-Output "$Succ successful, $Warn warnings and $Err errors from $($Succ+$Warn+$Err) replications."
Stop-Transcript
$file = "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
get-content $file |
Select-Object -Skip 18 |
set-content "$file-temp"
Move-Item "$file-temp" $file -Force
$emailrecipients = "boss#it.com";
$emailbody = Get-Content -Path "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt" -Raw
Send-MailMessage -to $emailrecipients -smtpserver smtp.domain.COM -from "$env:COMPUTERNAME#$env:userdnsdomain" -subject "DFSR Report for $(get-date -format dd/MM/yyyy) from $env:COMPUTERNAME" -body $emailbody;
Remove-Item "$([Environment]::GetFolderPath("Desktop"))\dfsr1.txt"
Theo is correct, the Param() code needs to be first thing in the script block. You can find more information about script blocks here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7

Test-connection for remote pcs

I have the following powershell with the aim of collecting a reg value from a list of remote machines, Ive tried using a test-connection to speed up process where devices are not online but the test-connection fails for all....
Here is the script:
clear
$servers = Get-Content -Path C:\ukdeviceswin10.txt
$PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$'
$ProfileList = ForEach ($server in $servers)
{ if(test-connection $server -quiet -count 1)
{
invoke-command -computername $server {gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} |
Select #{name="SID";expression={$_.PSChildName}}}}
foreach ($profile in $profilelist) {
$profile = $profile -replace ".$"
$profile = $profile.substring(6,46)
$profile = $profile -replace "[;]"
$profile = $profile -replace "[ ]"
$profiletest = $profile.substring(0,8)
if ($profiletest -eq "S-1-5-21"){
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::Users, "$server")
$profilekey = $profile + "\\SOFTWARE\\Microsoft\\Office\\Outlook\\Addins\\CofenseOutlookReporter.AddinModule"
$RegKey= $Reg.OpenSubKey("$profilekey")
if ($regKey -eq $null){} else {$LoadbehaviourVersion = $RegKey.GetValue("LoadBehaviour")
$results = $server + " " + $profile + " " + $LoadbehaviourVersion | out-file -filepath c:\dell\regresults.txt -Append
}
$RegKey=""
$LoadbehaviourVersion=""
}
}
}

Powershell Workflow: Using if/else statement?

I am trying to use a workflow in order to make a script run faster by running a ping on several machines in parallel. Where I am getting stuck is trying to use my if/else statement within the workflow and adding log entries based on the results of the ping. Right now, it is not writing anything to the logs based on the ping results. Any assistance would be greatly appreciated. I am open to suggestions for different methods to do this, as long as they do not involve using a module. I want to stick with just Powershell code (I do not have permissions to install modules on the server where this would be running). Thank you! Here is my current code:
<#
.NOTES
===========================================================================
Created on: 10/6/2016 8:06 AM
Created by:
Organization:
Filename: Get-MPOSOfflinePrinters.ps1
===========================================================================
.DESCRIPTION
#>
$logfile = "D:\Logs\MPOSPrinterPingLog.txt"
$offlineprinters = "D:\Reports\MPOS\MPOSOfflinePrinters.txt"
If (Test-Path $logfile) {Remove-Item $logfile}
If (Test-Path $offlineprinters) {Remove-Item $offlineprinters}
Add-Content $logfile "Setting local path"
$localPath = "C:\Temp\MPOS"
Add-Content $logfile "Gathering server list"
$serverList = (Get-ADComputer -Filter "Name -like 'Q0*P30' -or Name -like 'Q0*P32'" -SearchBase "OU=Print,OU=Prod,OU=POS,DC=COMPANY,DC=NET").name | Sort-Object | Out-File C:\Temp\MPOS\MPOSPrintServers.txt
$serverListPath = "C:\Temp\MPOS\MPOSPrintServers.txt"
#Retrieve a list of MPOS Print servers from text file and set to $serverNames
Add-Content $logfile "Compiling text file"
$serverNames = Get-Content -Path $serverListPath
#Iterate through each of the server names
foreach ($serverName in $serverNames) {
Add-Content $logfile "Processing $serverName"
#Check if the server is online before doing the remote command
If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Add-Content $logfile "$serverName is online"
#copy config file from MPOS print to local server for processing
$timestamp1 = (Get-Date -Format g)
Add-Content $logfile "$timestamp1 - Copying xml file from server to local path"
$configPath = "\\$($serverName)\C$\ProgramData\Microsoft\Point Of Service\Configuration\Configuration.xml"
Copy-Item $configPath $localPath
#process xml file to parse IP addresses for ping
$timestamp2 = (Get-Date -Format g)
Add-Content $logfile "$timestamp2 - Processing xml file from $serverName to parse data to csv"
$xml = [xml](Get-Content C:\Temp\MPOS\Configuration.xml)
$PrinterNames = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{LogicalName=$_.LogicalName.Name}}
$PrinterIPs = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{HardwarePath=$_.HardwarePath}}
workflow Test-WFConnection {
param(
[string[]]$PrinterIPs
)
foreach -parallel ($PrinterIP in $PrinterIPs) {
$pingableIP = $PrinterIP.HardwarePath
If (Test-Connection -ComputerName $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $logfile -Value ("$timestamp3 - $serverName, $pingableIP is online and pingable")
} #If (Test-Connection $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
Else {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $offlineprinters -Value ("$timestamp3 - $serverName, $pingableIP is offline!")
} #Else
} #foreach -parallel ($PrinterIP in $PrinterIPs) {
} #workflow Test-WFConnection {
Test-WFConnection -PSComputerName $PrinterIPs
} #If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Else {
Add-Content $logfile "$serverName is offline!"
} #Else
} #foreach ($serverName in $serverNames) {

Script Drops on all SQL Agent Jobs

I am new to powershell and I am trying to script out all SQL agent jobs. I have found a piece of code that does that thanks to ENRIQUE at SOLID QUALITY MENTORS.
My question is, how do I script a if exists, drop for every job?
Options.ScriptJobs does not seem to do what I think it should do?
param([string]$serverName,[string]$jobNameFile)
function script-SQLJobs([string]$server,[string]$jobNameFile)
{
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
$srv = New-Object Microsoft.SqlServer.Management.Smo.Server("$server")
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$scrp = New-Object Microsoft.SqlServer.Management.Smo.Scripter($srv)
$scrp.Options.ScriptDrops = $TRUE
$scrp.Options.WithDependencies = $TRUE
$jobNameFile = "C:\SQLJOBS\Jobs.sql"
remove-item $jobNameFile
$jobs = $srv.JobServer.get_Jobs()
$jobs=$jobs | Where-Object {$_.Name -notlike "sys*"}
foreach($job in $jobs)
{
$script=$job.Script()
$script >> $jobNameFile
"GO" >> $jobNameFile
}
}
script-SQLJobs $serverName $jobNameFile
Many Thanks.
You could exclude scripts that do not match the words 'drop table'. For example:
$srv.JobServer.Jobs | Where-Object {$_.Name -notlike "sys*"} | Foreach-Object{
$script = $_.Script()
if($script -notmatch 'DROP TABLE')
{
$script+ "`nGO`n"
}
} | Out-File $jobNameFile
Another (cosmetic) option would be to check all job steps command:
$srv.JobServer.Jobs | Where-Object {$_.Name -notlike "sys*"} | Foreach-Object{
$cmd = $_.JobSteps | select -expand Command
if($cmd -match 'DROP TABLE')
{
$_.script()+ "`nGO`n"
}
} | Out-File $jobNameFile
You need to provide your script options object to the script method:
$script=$job.Script($scrp)
Here is a Powershell script copied from http://www.johnsansom.com/script-sql-server-agent-jobs-using-powershell/ which has been extended to do what you want.
# Date: 16/02/14
# Author: John Sansom
# Description: PS script to generate all SQL Server Agent jobs on the given instance.
# The script accepts an input file of server names.
# Version: 1.1
#
# Example Execution: .\Create_SQLAgentJobSripts.ps1 .\ServerNameList.txt
param([String]$ServerListPath)
#Load the input file into an Object array
$ServerNameList = get-content -path "Servers.txt"
#$ServerNameList = get-content -path $ServerListPath
#Load the SQL Server SMO Assemly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
#Create a new SqlConnection object
$objSQLConnection = New-Object System.Data.SqlClient.SqlConnection
#For each server in the array do the following..
foreach($ServerName in $ServerNameList)
{
Try
{
$objSQLConnection.ConnectionString = "Server=$ServerName;Integrated Security=SSPI;"
Write-Host "Trying to connect to SQL Server instance on $ServerName..." -NoNewline
$objSQLConnection.Open() | Out-Null
Write-Host "Success."
$objSQLConnection.Close()
}
Catch
{
Write-Host -BackgroundColor Red -ForegroundColor White "Fail"
$errText = $Error[0].ToString()
if ($errText.Contains("network-related"))
{Write-Host "Connection Error. Check server name, port, firewall."}
Write-Host $errText
continue
}
#IF the output folder does not exist then create it
$OutputFolder = ".\$ServerName"
$DoesFolderExist = Test-Path $OutputFolder
$null = if (!$DoesFolderExist){MKDIR "$OutputFolder"}
#Create a new SMO instance for this $ServerName
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $ServerName
#Script out each SQL Server Agent Job for the server
foreach($job in $srv.JobServer.Jobs)
{
Write-Host $job.Name
$script = ""
$script = $script + "-- Uninstall the job" + "`r`n"
$script = $script + "DECLARE #jobId binary(16)" + "`r`n"
$script = $script + "SELECT #jobId = job_id FROM msdb.dbo.sysjobs WHERE (name = N'$job')" + "`r`n"
$script = $script + "IF (#jobId IS NOT NULL)" + "`r`n"
$script = $script + "BEGIN" + "`r`n"
$script = $script + " EXEC msdb.dbo.sp_delete_job #job_id=#jobId, #delete_unused_schedule=1" + "`r`n"
$script = $script + "END" + "`r`n"
$script = $script + "GO`r`n"
$script = $script + "`r`n"
$script = $script + "-- Install the job" + "`r`n"
$script = $script + $job.Script()
$script = $script + "GO`r`n"
$fileName = $job.Name -replace '\\', ''
$script | out-file ".\$OutputFolder\$fileName.sql"
}
}
NOTE: You don't actually want to use the SMO created DROP command because it is dependent on the job id, which makes the resulting script non-reusable.