In powershell I am trying to do the following:
$name = "computername"
#get installed programs
Write-Host "****APPLICATIONS"
gwmi win32_Product -ComputerName $name | select name
#gets services
write-host "****SERVICES"
Get-Service -ComputerName $name | ft
the expected output would be
****APPLICATIONS
name
of
app
****SERVICES
running services here
more services here
the actual result is
****APPLICATIONS
****SERVICES
name
of
app
running services here
more services here
I have attempted to do start-job then wait-job , but running gwmi as a job seems to output nothing to the console and sending the output to a separate file defeats the purpose of other parts of the script
I also attempted to use start-sleep and it still finishes both write-host commands before proceeding
Try this:
$name = "computername"
Write-Host "`n****APPLICATIONS`n"
gwmi win32_Product -ComputerName $name | % {$_.name}
write-host "`n****SERVICES"
Get-Service -ComputerName $name | ft
If you want the results alphabetical:
$name = "computername"
Write-Host "`n****APPLICATIONS`n"
$apps = gwmi win32_Product -ComputerName $name | % {$_.name}
$apps | sort
write-host "`n****SERVICES"
Get-Service -ComputerName $name | ft
Param(
$ComputerName = 'AT805061'
)
# Get installed programs
Write-Host "`n****APPLICATIONS`n"
Get-WmiObject win32_Product -ComputerName $ComputerName | Select-Object -ExpandProperty Name | Sort-Object
# Get services
Write-Host "`n****SERVICES`n"
Get-Service -ComputerName $ComputerName | Where-Object -Property Status -eq -Value Running | Select-Object -ExpandProperty Name | Sort-Object
Related
The majority of this code was pulled from a blog online, but I think it's exactly the way I need to be tackling this. I want to get the top 4 machines from an OU based on uptime, and run a script that lives on each of the top 4 machines. I know that the problem involves the Array losing access to the Get-ADComputer properties, but I'm unsure of how to pass these new properties back to their original objects. This works as expected until it gets to the foreach loop at the end.
$scriptBlock={
$wmi = Get-WmiObject -Class Win32_OperatingSystem
($wmi.ConvertToDateTime($wmi.LocalDateTime) – $wmi.ConvertToDateTime($wmi.LastBootUpTime)).TotalHours
}
$UpTime = #()
Get-ADComputer -Filter 'ObjectClass -eq "Computer"' -SearchBase "OU=***,OU=***,OU=***,DC=***,DC=***" -SearchScope Subtree `
| ForEach-Object { $Uptime += `
(New-Object psobject -Property #{
"ComputerName" = $_.DNSHostName
"UpTimeHours" = (Invoke-Command -ComputerName $_.DNSHostName -ScriptBlock $scriptBlock)
}
)
}
$UpTime | Where-Object {$_.UpTimeHours -ne ""} | sort-object -property #{Expression="UpTimeHours";Descending=$true} | `
Select-Object -Property ComputerName,#{Name="UpTimeHours"; Expression = {$_.UpTimeHours.ToString("#.##")}} | Select-Object -First 4 |`
Format-Table -AutoSize -OutVariable $Top4.ToString()
foreach ($Server in $Top4.ComputerName) {
Invoke-Command -ComputerName $Server -ScriptBlock {HOSTNAME.EXE}
}
I'm not married to Invoke-Command in the last foreach but am having the same issues when I try to use psexec. Also, I'm running hostname.exe as a check to make sure I'm looping through the correct machines before I point it at my script.
Here's a streamlined version of your code, which heeds the comments on the question:
# Get all computers of interest.
$computers = Get-ADComputer -Filter 'ObjectClass -eq "Computer"' -SearchBase "OU=***,OU=***,OU=***,DC=***,DC=***" -SearchScope Subtree
# Get the computers' up-times in hours.
# The result will be [double] instances, but they're also decorated
# with .PSComputerName properties to identify the computer of origin.
$upTimes = Invoke-Command -ComputerName $computers.ConputerName {
((Get-Date) - (Get-CimInstance -Class Win32_OperatingSystem).LastBootUpTime).TotalHours
}
# Get the top 4 computers by up-time.
$top4 = $upTimes | Sort-Object -Descending | Select-Object -First 4
# Invoke a command on all these 4 computers in parallel.
Invoke-Command -ComputerName $top4.PSComputerName -ScriptBlock { HOSTNAME.EXE }
Could you please inform me how can I make my script to format its output in a single table for all "services" per "server"?
Please find my current PowerShell script below:
$serverList = gc computer.txt
$serviceList = gc service.txt
if ((Test-Path OUTPUT.txt) -eq $true) {
Write-Host "Deleting existing OUTPUT file"
Remove-Item OUTPUT.txt
}
ForEach ($server in $serverList)
{
$style = #{Expression={$server};Label="Server Name";width=30}, `
#{Expression={$_.Name};Label="Service Name";width=20}, `
#{Expression={$_.StartMode};Label="StartMode";width=10}, `
#{Expression={$_.State};Label="State";width=10}, `
#{Expression={$_.ProcessId};Label="ProcessId";width=10}
Write-Host "Starting Service Check on $server"
ForEach ($service in $serviceList)
{
Get-WmiObject -Class Win32_Service -ComputerName $server -Filter "Name='$service'" | Select-Object -Property Name, StartMode, State, ProcessId | Format-Table $style | Out-File OUTPUT.txt -Append
}
Write-Host "Service Check Completed on $server"
}
WMI appends the property PSComputername. You can just append it in your select-object-command like so:
Get-WmiObject -Class Win32_Service -ComputerName $server -Filter "Name='$service'" | Select-Object -Property PScomputername, Name, StartMode, State, ProcessId | Format-Table $style | Out-File OUTPUT.txt -Append
im new beginner of powershell, now i have two script, one is get remote server's IPs, another one is get remote server's specific service start time, i need to show remote server's IP and specific service start time, can someone guide me how to merge these two script.
below is my two script.
$servers = gc -path D:\Ted\Computers.txt
$Job = get-wmiobject win32_networkadapterconfiguration -computer $servers -filter "IPEnabled='True'" -asjob
$results = $job | receive-job
$results
get-job | wait-job
receive-job job* | select IPAddress
and another one for get service start time is
$servers = gc -path D:\Ted\Computers.txt
$check = get-wmiobject win32_process -computer $servers -Filter "Name='aspnet_state.exe'" -asjob
$results = $check | receive-job
$results
get-job | wait-job
receive-job job* | Select-Object name, processId, #{Name="StartTime"; Expression={ $_.ConvertToDateTime( $_.CreationDate )}}
at last i need know one thing, If I use asjob to this script, that means it is multi-threaded execution?
sorry for my poor english, thank you for your kindly help.
There is probably a cleaner way to do this, but here is my take on your problem. It looks as if you need some way to correlate each computer to the output of the two WMI queries. If it is a requirement to run this in parallel using jobs, it will take a bit more work, but here is a serial version.
Get-Content -Path D:\Ted\Computers.txt | ForEach-Object {
$ip = Get-WmiObject Win32_NetworkAdapterConfiguration -Computer $_ -Filter "IPEnabled='True'" | Select-Object IPAddress
$process = Get-WmiObject Win32_Process -Computer $_ -Filter "Name='aspnet_state.exe'" | Select-Object Name, ProcessId, #{ Name="StartTime"; Expression = { $_.ConvertToDateTime($_.CreationDate) } }
#{
Computer = $_
Ip = $ip
Name = $process.Name
ProcessId = $process.ProcessId
StartTime = $process.StartTime
}
}
A parallel version would be something along the lines of this:
# A collection that stores all the jobs
$AllJobs = #()
# A collection that stores jobs correlated with the computer
$ComputerJobs = Get-Content -Path D:\Ted\Computers.txt | ForEach-Object {
$ipJob = Get-WmiObject Win32_NetworkAdapterConfiguration -Computer $_ -Filter "IPEnabled='True'" -AsJob
$AllJobs += $ipJob
$processJob = Get-WmiObject Win32_Process -Computer $_ -Filter "Name='aspnet_state.exe'"
$AllJobs += $processJob
#{
Computer = $_
IpJob = $ipJob
ProcessJob = $processJob
}
}
# Wait for everything to complete
Wait-Job -Job $AllJobs
# Iterate the correlated collection and expand the results
$ComputerJobs | ForEach-Object {
$ip = Receive-Job -Job $_.IpJob | Select-Object IPAddress
$process = Receive-Job -Job $_.ProcessJob | Select-Object Name, ProcessId, #{ Name="StartTime"; Expression = { $_.ConvertToDateTime($_.CreationDate) } }
#{
Computer = $_.Computer
Ip = $ip
Name = $process.Name
ProcessId = $process.ProcessId
StartTime = $process.StartTime
}
}
I have the following short script to grab serial numbers of computers and monitors in an OU, which works fine:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | %{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};
This script doesn't check to see if the computer is online before attempting to fetch the WMIObject, so if a computer is offline it takes ages before the RPC call times out.
I tried to modify the script to use the Test-Connection cmdlet before trying to get the WMIObject:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | `
if (Test-Connection -ComputerName $_ -Quiet) {
%{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};}
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
I'm sure I'm doing something syntactically stupid. Can someone point me in the right direction?
You can't pipe directly to an if statement, only to cmdlets.
Put the if statement inside the ForEach-Object block (% is an alias for ForEach-Object):
... | Select-Object -Expand Name | `
%{
if (Test-Connection -ComputerName $_ -Quiet) {
# Get-WmiObject in here
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
}
If you don't care about writing each machine's status to the host, you could also filter out offline computers with Where-Object(alias ?):
... | Select-Object -Expand Name | ?{
Test-Connection $_ -Quiet
} | % {
Get-WmiObject -ComputerName $_
}
In addition to the answer from #Mathias R. Jessen, you can get rid of the backticks for line continuation.
They are not needed if the end of the line infers there is another block of code required for the statement. Like | or { or (.
"foo", "bar" |
% {$_}
works just fine...
I can only get the command to return the services on the first computer in the text file.
Is there a better way than for-each for this task?
Get-Service *vault* -ComputerName (Get-Content c:\users\sean\desktop\js.txt) | select name,status,machinename | sort machinename | format-table -autosize
Try it without the get-content. Try this:
Get-Service *vault* -ComputerName c:\users\sean\desktop\js.txt | select name,status,machinename | sort machinename | format-table -autosize
If that doesn't work, then try:
$Computers = Get-Content c:\users\sean\desktop\js.txt
Get-Service *vault* -computername $Computers | Select name,status,machinename |sort machinename |format-table -autosize
If you are eager for a one-liner then try this:
Get-Content c:\users\sean\desktop\js.txt | Get-Service *vault* | Select name,status,machinename |sort machinename |format-table -autosize
I would try the top one first. I would test, but I don't have access to anything I can do a proper test right now.
$Computers = get-content .\desktop\test.txt
$Service = "Vault"
foreach ($computer in $computers) {
$computer
$Servicestatus = get-service -name $Service -ComputerName $computer
}
$Servicestatus | select-object Name,Status,MachineName | format-table -Autosize
This works for me, it gives me each of the computers in the text file, and it looks for the service.
This is what I use. I get the list of computers from an OU in AD.
Import-Module ActiveDirectory
$ou = "OU=Servers,DC=Domain,DC=com"
$servers = Get-ADComputer -Filter * -SearchBase $ou | select-object -expandproperty name
Foreach ($server in $servers){
$Data = Get-Service -ServiceName *IIS*,*TomCat*,*httpd* -ComputerName $server | select machinename,name | sort machinename | format-table -AutoSize
Write($Data) | Out-File .\WebServices.txt -Append
}
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service -ComputerName $Server -name -like "*vault*"
"-------------------"
}
Following a memory limitation limit with older versions of PowerShell, I was required to refresh my code:
Old code:
gwmi win32_service -computer $allcomputers | Select-Object __SERVER,Name,state,startmode,StartName
New code:
`$servers = Get-Content "computers.txt"
Foreach ($server in $servers) {
Get-WmiObject -Class WIN32_service -ComputerName $server |
Select-Object __SERVER,Name,state,startmode,StartName |
Export-Csv -path "Report.CSV" -NoTypeInformation -Append
}`
This is how you can get list of all services in your AD domain:
Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”} | ForEach-Object {Get-WmiObject -Class Win32_Service -Computer $_.Name}
More useful examples on this (get list of services for all computer listed in a text file, etc.):
https://www.action1.com/kb/list_of_services_on_remote_computer.html
Get-Service -ComputerName ... has a bug in PowerShell 2.0 that only returns the first computer. This is fixed in newer versions so if you upgrade to PowerShell 3.0 or newer, your original code will work fine.
As a workaround, use a foreach-loop to run Get-Service once for each computer:
Get-Content c:\users\sean\desktop\js.txt |
ForEach-Object { Get-Service -Name *vault* -ComputerName $_ } |
Select-Object -Property Name, Status, MachineName |
Sort-Object -Property MachineName |
Format-Table -AutoSize
Nick's solution totally doesn't work for me. I ended up writing a quick and dirty one that works:
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service *vault*
"-------------------"
}