So i have 2 variables
one include 4 services
service 1
service 2
service 3
service 4
second variable include all services which are running on computer
$servicesall = (Get-service | Group-Object -Property Name).Name
$servicestocheck = Get-Content
i want to log to file info like Service 1 is running, Service 2 is not running
I'm not sure if this is exactly what you're looking for but if you store the services you need in a text file you can do the following:
$services = Get-Content "C:\ServiceList.txt"
$statusReport = Get-Service -Name $services | Select-Object Name, Status
$statusReport | Out-File -path "c:\logfile.log"
Result:
Name Status
---- ------
spooler Running
winrm Stopped
wsearch Running
The name parameter in Get-Service -Name $services accepts an array of strings, which is why you don't need to do any sort of 'foreach' loop.
This may not be the best way to do this, but here you have something to work with:
$services = (
"vds",
"VSS",
"W32Time",
"abc",
"bcd"
)
foreach ($item in $services) {
$service = Get-Service -Name $item -ErrorAction SilentlyContinue
if ($service) {
$exist = $true
} else {
$exist = $false
$service = #{
status = "nope"
Name = $item
}
}
$service | select Status, Name, #{Name="Exist"; Expression={$exist}}
}
Result:
Status Name Exist
------ ---- -----
Stopped vds True
Stopped VSS True
Running W32Time True
nope abc False
nope bcd False
Related
I currently want to check if a list of processes are running, then display the result within a table such as:
Process Status
======= ======
Process 1 Running
Process 2 Not Running
Process 3 Running
I have the below code which produces an output showing each input and output as a string, but it looks messy depending on the length of the Process name.
$Node = Read-Host -Prompt 'Input Node name'
$Process = #("Process1", "Process2", "Process3")
$Process | foreach-object {if(!(Get-Process -Name $_ -ComputerName $Node - ErrorAction SilentlyContinue)) {"$_ - Not Running"} else {"$_ - Running"}}
I am at a loss. All help appreciated.
Better (faster) to make a single remoting call to get all the processes, than one per process, so do that and store all the results - at least the names of the processes.
The next part is non-trivial. The way PowerShell and the neatly formatted tables work, is by making one object (bundle of things all together) for each table row, with each object having properties for each column name.
# Processes to look for
$Process = #("Process1", "Process2", "Process3")
$Node = Read-Host -Prompt 'Input Node name'
# Get running processes, and only keep their names
$runningProcesses = Get-Process -ComputerName $Node -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Name
$Process | ForEach-Object {
# For each process name to look for, generate a hashtable of
# columns and their values,
# then cast it into a PS Object
[PSCustomObject]#{
'ProcessName' = $_
'Status' = if ($runningProcesses -contains $_) { "Running" } else { "Not Running" }
}
}
This gives a neat formatted table output, and is also structured data so you can feed the output of this into | ForEach-Object { $_.Status } and pick out the individual parts by name, something you can't do as neatly with your string formatted approach.
Try this:
$node = Read-Host -Prompt 'Input Node name'
$processList = "Process1", "Process2", "Process3"
$processList |
ForEach-Object {
[PsCustomObject]#{
NodeName = $node
ProcessName = $_
IsRunning = (Get-Process -Name $_ -ComputerName $node -ErrorAction SilentlyContinue | Select-Object -First 1) -ne $null
}
}
Output will be like this:
NodeName ProcessName IsRunning
-------- ----------- ---------
Node1 Process1 True
Node1 Process2 True
Node1 Process3 False
Basically as per screen-shot there are multiple worker processes are running on machine in IIS but we need w3wp which is running under Sitecore User Username.
We tried below PS script but getting blank value in User Name column
$processlist = get-process | where {$_.cpu -gt 5 -and $_.Name -eq 'w3wp'} |select Name, #{l="User name";e={$_.getowner().user}} | ft -AutoSize
foreach($proc in $processlist){
if($proc -eq "Sitecore User" ){
C:\Users\Administrator\Documents\someexe.exe $proc.Id "C:\Users\Administrator\Documents\output.dmp"
}
}
and finally we need to perform some action on process Id.
I suggest the following PoSh-Script that should give you all the necessary info and more:
# Ensure to import the WebAdministration module
Import-Module WebAdministration
# Declare the variables
$server = "localhost"
$search = "*"
$wmiQuery=[wmisearcher]"SELECT * FROM __Namespace where NAME like 'WebAdministration' or NAME like 'MicrosoftIISv2'"
$wmiQuery.Scope.Path = "\\" + $server + "\root"
$WebNamespace = $wmiQuery.Get()
# Checking if the the server has IIS installed
if($WebNamespace -like '*WebAdministration*')
{
"IIS found on $server"
$WPlist=Get-WmiObject -NameSpace 'root\WebAdministration' -class 'WorkerProcess' -ComputerName 'LocalHost'
# Loop through the list of active IIS Worker Processes w3wp.exe and fetch the PID, AppPool Name and the startTime
forEach ($WP in $WPlist)
{
if ($WP.apppoolname -like$search)
{
write-host "Found:""PID:"$WP.processid "AppPool_Name:"$WP.apppoolname
(get-process -ID $WP.processid|select starttime)
}
}
}
Else
{
write-host"WARNING: IIS not detected."
}
Ref: https://blogs.msdn.microsoft.com/webtopics/2015/11/28/query-the-active-worker-process-information-in-iis-7-x-using-powershell/
I have this function I'm using a foreach statement block to run against a number of machines:
function Get-InstalledApps ($appStr) {
$appWC = "*$appStr*"
if ([IntPtr]::Size -eq 4) {
$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
}
$getapps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }}
Foreach ($app in $getapps | where {$_.DisplayName -like $appWC}) {
[pscustomobject]#{Computer = ($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN)
AppName = ($app.displayname)
Publisher = ($app.Publisher)
DisplayVersion = ($app.DisplayVersion)
InstallDate = ($app.InstallDate)
UninstallString = ($App.UninstallString)}
}
}
Locally, it looks like this:
PS C:\windows\system32> Get-InstalledApps ibm | ft
Computer AppName Publisher DisplayVersion InstallDate UninstallString
-------- ------- --------- -------------- ----------- ---------------
Computer.domain.COM IBM Tivoli Storage Manager Client IBM 06.04.0001 20140807 MsiExec.exe /I{FF99015E-71B4-41AB-8985-67D99383A72A}
But when run remotely on some computers
(i.e:)
Invoke-Command -ComputerName $computer -ScriptBlock
${function:Get-InstalledApps} -ArgumentList $appStr
I get the above, however on others I get this:
Name Value
---- -----
UninstallString MsiExec.exe /I{68C09095-AC00-4541-B46B-0835F2BDB0CE}
Computer comp1.domain.com
Publisher IBM
InstallDate 20150122
DisplayVersion 07.01.0000
AppName IBM Tivoli Storage Manager Client
UninstallString MsiExec.exe /X{1316AC9A-7A5D-4866-B41F-4B3CF03CE52A}
Computer comp2.domain.com
Publisher IBM Corp.
InstallDate 20170226
DisplayVersion 9.2.7.53
AppName IBM BigFix Client
Without having a chance to verify PowerShell versions of some of the computers yet, I'm guessing the 2nd set of results may be as a result of being run against computers running < version 3.0.
Any way to force the output to display as a table (1st example output) on all computers?
I'm guessing the 2nd set of results may be as a result of being run against computers running < version 3.0.
If you are running that on systems that are not at least version 3 then your [pscustomobject] cast would fail since that was introduced in v3. I would have expected that to just trigger an error but instead it appears to be returning the hashtable. A compatible solution would be to use new-object instead.
New-Object -TypeName PSCustomObject -Property #{
Computer = ($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN)
AppName = ($app.displayname)
Publisher = ($app.Publisher)
DisplayVersion = ($app.DisplayVersion)
InstallDate = ($app.InstallDate)
UninstallString = ($App.UninstallString)
}
Thanks Matt.
That worked, which is my preferred method.
if the app wasn't installed or the host was offline, a couple of variations of IF statements didn't seem to pick up the output at another point in the script (only displayed if it was installed) and returned as a blank line, however this seemed to be picked up by the statement blocks:
function Get-InstalledApps ($appStr) {
$appWC = "*$appStr*"
if ([IntPtr]::Size -eq 4) {
$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
}
$getapps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }}
$getapps | where {$_.DisplayName -like $appWC} | Select #{n='Computer';e={$env:COMPUTERNAME + "." + $env:USERDNSDOMAIN}},Displayname,Publisher,DisplayVersion,InstallDate,UninstallString
}
I have a script working for Windows 2012 (PowerShell v4) but it has to work also for Windows 2008 (PowerShell v2), what is the equivalent of the cmdlet "Resolve-DNSName" for Windows 2008?
Resolve-DnsName -Name client01 -Server server01
I know it exists the same for nslookup and this is what I would like as a cmdlet (one-liner, with no input required from my part)
nslookup
server server01
client01
The following works for DNS resolution but is missing the -server parameter :
[Net.DNS]::GetHostEntry("MachineName")
Thanks
Unfortunately there isn't a way to do this natively in powershell prior to Version 4 in Windows 8.1 or Server 2012. There are .NET methods however:
https://stackoverflow.com/a/8227917/4292988
The simplest solution in powershell is to call nslookup, and cleanup the output
&nslookup.exe client01 server01
I removed select-string from the original sample, it left less to work with
The function you posted following mine doesnt work very well, and will never work in PowershellV2, [PSCustomObject] wasn't supported until v3. Furthermore if you send a dns query that would normally return a single address, it returns nothing. For queries with aliases, it returns the aliases where the ipaddress should be. Test Resolve-DnsName2008 -name www.stackoverflow.com -server 8.8.8.8.
The Following is a function that should do what your asking, at least for ipv4addresses:
function Resolve-DnsName2008
{
Param
(
[Parameter(Mandatory=$true)]
[string]$Name,
[string]$Server = '127.0.0.1'
)
Try
{
$nslookup = &nslookup.exe $Name $Server
$regexipv4 = "^(?:(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)\.){3}(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)$"
$name = #($nslookup | Where-Object { ( $_ -match "^(?:Name:*)") }).replace('Name:','').trim()
$deladdresstext = $nslookup -replace "^(?:^Address:|^Addresses:)",""
$Addresses = $deladdresstext.trim() | Where-Object { ( $_ -match "$regexipv4" ) }
$total = $Addresses.count
$AddressList = #()
for($i=1;$i -lt $total;$i++)
{
$AddressList += $Addresses[$i].trim()
}
$AddressList | %{
new-object -typename psobject -Property #{
Name = $name
IPAddress = $_
}
}
}
catch
{ }
}
I use this code to input FQDNs one per line and output respective IPs.
$Server = Get-Content servers.txt
$OutArray = #()
$output = foreach ($Server in $Server) {
$IP = [System.Net.Dns]::GetHostAddresses($Server)
$OutArray += $Server + " " + $IP.IPAddressToString
}
$OutArray | Out-File IPs.txt
The problem is that if I use :
&nslookup.exe client01 server01 | select-string "Name", "Addresses"
It will only display the first record, in my case I had 5 records found and only one displayed.
The solution I found works very well :
function Resolve-DNSName2008
{
Param
(
[string]$Name,
[string]$Server
)
$nslookup = &nslookup.exe $Name $Server
$name = [string]($nslookup | Select-String "Name")
$nameClean = ([regex]::match($name,'(?<=:)(.*\n?)').value).Trim()
$addresses = (([regex]::match($nslookup,'(?<=Addresses:)(.*\n?)').value).Trim()).Split(' ')
$addressesClean = $addresses.Split('',[System.StringSplitOptions]::RemoveEmptyEntries) | Sort-Object
$addressesClean | %{
[PSCustomObject]#{
Name = $nameClean
IPAddress = $_
}
}
}
Usage:
Resolve-DNSName2008 -Name server.domain.com -Server 10.0.0.0
Output:
Name IPAddress
---- ---------
server.domain.com 10.0.0.1
server.domain.com 10.0.0.2
server.domain.com 10.0.0.3
server.domain.com 10.0.0.4
server.domain.com 10.0.0.5
I am having an issue with the way that my ping results "roll" out on the screen. I am using this code:
$servers = "192.168.2.10","192.168.2.80","192.168.2.254"
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
$testconnection = (Test-Connection $server -Count 1 -ea 0)
$response = ($testconnection | select ResponseTime)
if ($response)
{
$status["Results"] = "Up"
$status["Responsetime"] = $response
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
$collection | Export-Csv -Path ".\ServerStatus.csv" -NoTypeInformation
I would to like create a loop for the ResponseTime
The code that I am using now gives one response.
When I give a count of 2, it prints the ResponseTime next to eachother per IP-adres.
Output:
TimeStamp Responsetime Results ServerName
--------- ------------ ------- ----------
2014-10-22T23:30:17 {#{ResponseTime=6}, #{ResponseTime=4}} Up 192.168.2.10
2014-10-22T23:30:18 Down 192.168.2.80
2014-10-22T23:30:25 {#{ResponseTime=1}, #{ResponseTime=3}} Up 192.168.2.254
What I want is, that the script prints each ResponseTime under eachother like this:
TimeStamp Responsetime Results ServerName
--------- ------------ ------- ----------
2014-10-22T23:11:50 #{ResponseTime=419} Up 192.168.2.10
2014-10-22T23:11:51 #{ResponseTime=415} Up 192.168.2.10
2014-10-22T23:11:51 Down 192.168.2.80
2014-10-22T23:11:52 #{ResponseTime=470} Up 192.168.2.254
2014-10-22T23:11:52 #{ResponseTime=7} Up 192.168.2.254
Or like this:
TimeStamp Responsetime Results ServerName
--------- ------------ ------- ----------
2014-10-22T23:11:50 #{ResponseTime=419} Up 192.168.2.10
2014-10-22T23:11:51 Down 192.168.2.80
2014-10-22T23:11:51 #{ResponseTime=415} Up 192.168.2.254
2014-10-22T23:11:52 #{ResponseTime=470} Up 192.168.2.10
2014-10-22T23:11:51 Down 192.168.2.80
2014-10-22T23:11:52 #{ResponseTime=7} Up 192.168.2.254
It doesn't matter which one, my preference is the second one
Could you please help me with this matter. Even if it is not possible tell me aswell.
Thank you,
Chris
I'll chime in late, not because the other answer are wrong by any means, they are both functional, but more so because nobody has pointed out that you are recreating the wheel.
You test the connection, and specify an erroraction for it that silently continues leaving your variable null. Then you have to test to see if the variable has results, and treat it one way, or if it doesn't treat it another way. What you have just done is made your own Try/Catch scenario. If you actually use the error to stop you can use the built in Try/Catch. Consider this approach:
$servers = "www.google.com","localhost","www.amazon.com"
$collection = #()
foreach ($server in $servers)
{
Try{
$testconnection = Test-Connection $server -Count 2 -ErrorAction Stop
$testconnection | ForEach{$collection += New-Object PSObject -Property ([ordered]#{
'TimeStamp' = Get-Date -Format s
'Server' = $server
'ResponseTime' = $_.responsetime
'Results' = 'Up'})
}
}
Catch{
$collection += New-Object PSObject -Property ([ordered]#{
'TimeStamp' = Get-Date -Format s
'Server' = $server
'ResponseTime' = $null
'Results' = 'Unreachable'
})
}
}
$collection #| Export-Csv -Path ".\ServerStatus.csv" -NoTypeInformation
That tries to ping the server, and if it can it adds a custom object to the $collection array with the desired information. If the ping fails it also adds an object to the $collection showing that the server was unreachable.
Also, you had $collection = $(). I assume you were trying to create an empty array, which is correctly done $collection = #() (corrected in my suggested code). Now, I commented out the Export-CSV so I could see the results. This is what I saw:
TimeStamp Server ResponseTime Results
--------- ------ ------------ -------
2014-10-22T17:54:22 www.google.com 9 Up
2014-10-22T17:54:22 www.google.com 12 Up
2014-10-22T17:54:23 localhost 0 Up
2014-10-22T17:54:23 localhost 0 Up
2014-10-22T17:54:27 www.amazon.com Unreachable
Amazon didn't let me ping it, so it shows as unreachable.
Moving on to why your desired results are not practical... What you describe shows you pinging your servers and getting results from them at non-consecutive times. To do that you would have to do -count 1, and loop through the ForEach loop twice, so it would ping server 1 for 1 result, then server 2 for 1 result, then server 3 for 1 result. Then it would go back and ping server 1 for a second result, then server 2 for a second result, and then server 3 for a second result. If you wanted to do that you could I suppose, and it should give you your desired results, you would have to do something like this:
$servers = "www.google.com","localhost","www.amazon.com"
$collection = #()
$count = 2
for($i=1;$i -le $count;$i++){
ForEach($server in $servers){
do stuff to ping servers as described above, except change -count to 1
}
}
$collection | export-CSV '.\ServerStatus.csv' -notype
That will give you your desired results, but it is slower. If you have to run this against more than a few servers it will be noticeably slower. For just those three servers listed it made the entire process go from taking 3.7240945 seconds to taking 7.6104075 seconds (roughly double).
Instead of
$response = ($testconnection | select ResponseTime)
if ($response)
{
$status["Results"] = "Up"
$status["Responsetime"] = $response
}
do
if($testconnection)
{
$testconnection | % {
$status = #{"ServerName" = $server; "TimeStamp" = (Get-Date -f s); "Results" = "Up"; "Responsetime"= $_.responsetime};
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus;
$collection += $serverStatus }
}
else
{
$status = #{"ServerName" = $server; "TimeStamp" = (Get-Date -f s); "Results" = "Down"};
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus;
$collection += $serverStatus
}
The problem is that $testconnection or in your case $response is an array if the count of Test-Connection is greater then 1, so you have to loop through it and add the single entries to your collection.
Also to get the Value instead of the gibberish you get you have to call the .responsetime property.
In hopes I didn't make it too complicated I present this solution
$servers = "10.50.10.100","8.8.8.8","169.254.54.1"
$servers | ForEach-Object{
$server = $_
$timeStamp = (Get-Date -f s)
$testconnection = Test-Connection $server -Count 2 -ErrorAction 0
If(!$testconnection){
$props = #{
Server = $server
TimeStamp = $timeStamp
ResponseTime = ""
Results = "Down"
}
New-Object -TypeName PSObject -Property $props
} Else {
$testconnection | ForEach-Object{
$_ | Select-Object #{l='Server';e={$server}},#{l='TimeStamp';e={$timeStamp}},#{l='ResponseTime';e={$_.ResponseTime}},#{l='Results';e={"Up"}}
}
}
} | Export-Csv -Path ".\ServerStatus.csv" -NoTypeInformation
So your logic is still here but as you can see some things have been changed. Paul was right, in that you needed to loop for each ResponseTime element you had. I also have done that but with a different approach that, if nothing else, will show you some of the Power in PowerShell. A break down of the code
Pipe $servers into a ForEach-Object. ForEach in works fine however I wanted to skip the saving the variables and just output straight to Export-CSV which is why I changed it.
So if you use Test-Connection on a server that does not exist or errors for some reason then you need to create an object to represent that. Using the desired properties, build a object with required values. This is output to the pipe instead of using a temporary variable.
When a connection test is successful then we need to output a number or variables to match the number of returns.
Continuing from #3 we use Select-Object to output the desired values. l stand for label and e for expression. Yes you could easily just use another $props variable. Just illustrating another option.
Since we changed the ForEach in the first step we can just output straight to Export-CSV
Sample output
Server TimeStamp ResponseTime Results
------ --------- ------------ -------
10.50.10.100 2014-10-22T20:22:01 0 Up
10.50.10.100 2014-10-22T20:22:01 0 Up
8.8.8.8 2014-10-22T20:22:02 43 Up
8.8.8.8 2014-10-22T20:22:02 39 Up
169.254.54.1 2014-10-22T20:22:03 Down