I've been trying to develop a powershell script to query a particular OU in AD and then get the services on each of those services including name, status, startmode, startname, description. I've gotten really close but not getting the results I expect. Can someone help?
The problem is if I run the command for one server specifically, I get the results I want. If I run for all servers in an OU it does not find any unique startnames (only the local accounts). I want a single CSV with the server name first
Script for single computer (getting results we want):
$ServerName = “server01”
Get-WmiObject win32_service -ComputerName $servername | Select #{N="ServerName";E={$ServerName}}, Name, Status, StartMode, StartName, description | Export-Csv "server_Services.csv" -NoTypeInformation
Script for querying OU (only returning services with local accounts):
$ServerOU = 'OU=Servers,DC=some,DC=domain'
$ServerList = Get-QADComputer -SearchRoot $ServerOU -SearchScope Subtree
$ServerList | ForEach {
$ServerNameTemp = $_.Name
Get-WMIObject Win32_Service |
Select #{N="ServerName";E={$ServerNameTemp}}, StartName, Name, StartMode, Status, description
} | Export-Csv -NoTypeInformation $ExportFile
This should do the trick i think but th following would wanna make a file for each server in the OU, it would keep overwriting the same file.
I also Changed Get-QADComputer into Get-ADComputer no specific need for quest powershell for this one :)
$ServerOU = 'OU=Servers,DC=some,DC=domain'
$ServerList = Get-ADComputer -SearchBase $ServerOU -Filter *
ForEach ($Server in $Serverlist)
{
Invoke-Command -ComputerName $Server.Name -ScriptBlock {
Get-WMIObject Win32_Service |
Select #{N="ServerName";E={[System.Net.Dns]::GetHostName()}}, StartName, Name, StartMode, Status, description}
} | Export-Csv -NoTypeInformation $ExportFile
This way it would send ti all to a variable first and then to a file.
$ServerOU = 'OU=Servers,DC=some,DC=domain'
$Services = #()
$ServerList = Get-ADComputer -SearchBase $ServerOU -Filter *
ForEach ($Server in $Serverlist)
{
$Services += Invoke-Command -ComputerName $Server.Name -ScriptBlock {
Get-WMIObject Win32_Service |
Select-Object #{N="ServerName";E={[System.Net.Dns]::GetHostName()}}, StartName, Name, StartMode, Status, description }
}
$Services | Export-Csv -NoTypeInformation $Exportfile
PS Remoting is automatically enabled from win 2012. If you are using any Win servers prior to that, you need to enable to WinRM services, to enable powershell remoting. This is very important, if you are trying to use "Invoke-Command" or any of those "*-Session" cmdlets against those servers.
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 }
I have used the below code to connect with multiple servers which I have mentioned on to the text file. How can I connect ad servers and get the output in CSV format. if any changes required let me know
$ServerList = Get-Content "C:\temp\Servers.txt"
ForEach($computername in $ServerList)
{
Get-CimInstance -ClassName SoftwareLicensingProduct -ComputerName $ComputerName |`
Where{$_.PartialProductKey -and $_.Name -like "*Windows*"} | Select `
#{Expression={$_.PSComputerName};Name="ComputerName"},`
#{Expression={$_.PropertiesName};Name="Properties"} ,`
#{Expression={$OperatingSystem[$($_.OperatingSystem)]};Name="OperatingSystem"}
}
Get-ADComputer -Filter * -Properties name, OperatingSystem | Select name, OperatingSystem|Export-CSV -NoType c:\temp\JCcomputers.csv
i got a client who wants to find all of the companys installed programs i wrote a script but i dont want the script show me the same same programs for each comuter every time,i want to see overall installations
$computers = get-adcomputers -filter *
foreach($computer in $computers){
Get-ItemProperty
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Format-
Table –AutoSize}
I did not test this, but you can try
$computers = (Get-ADComputer -Filter *).DNSHostName # or use .Name or .CN
$software = Invoke-Command -ComputerName $computers {
Get-ItemProperty -Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
$software | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate -Unique |
Format-Table -AutoSize
P.S.1 You need to have admin permissions on all computers to do this
P.S.2 Don't forget there is also HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
Apparently you are running into problems where computers are off-line.
To overcome that, you need to add a loop so you can test if a machine is reachable or not.
$computers = (Get-ADComputer -Filter *).Name # or use .CN
# loop through the collection and (if reachable) get the software list
$result = foreach ($computer in $computers) {
# test if the computer is online
if (Test-Connection -ComputerName $computer -Count 1 -Quiet) {
# output the properties you need to get collected in variable $result
Invoke-Command -ComputerName $computer {
Get-ItemProperty -Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
} | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate
}
else {
Write-Warning "Computer $computer is off-line"
}
}
$software = $result | Select-Object * -Unique
# output to console
$software | Format-Table -AutoSize
# output to CSV file
$software | Export-Csv -Path 'D:\Software.csv' -NoTypeInformation
Overall my goal is to get the VNC version for a list of remote computers along with the uninstall GUID so I can remotely uninstall VNC Viewer from certain computers. I have used the Get-WmiObject -Class Win32_Product but that is extremely slow.
I have the following script but in the results it includes the name of the select-object parameter.
$computers = Get-Content -Path "C:\Computers.txt"
$Results = #()
ForEach ($Computer in $Computers) {
$Results += New-Object PSObject -Property #{
"ComputerName" = $Computer
"Name" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayName
"DisplayVersion" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayVersion
"ModifyPath" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object ModifyPath
"Vendor" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object Publisher
}
}
$Results | Select-Object ComputerName,Name,DisplayVersion,ModifyPath,Vendor | Sort-Object ComputerName | Export-Csv C:\VNC.csv -notype ;
My results look like this:
ComputerName : ComputerName
Name : #{DisplayName=VNC Viewer 5.2.3}
DisplayVersion : #{DisplayVersion=5.2.3}
ModifyPath : #{ModifyPath=MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}}
Vendor : #{Publisher=RealVNC Ltd}
I would want it to look like this:
ComputerName : ComputerName
Name : VNC Viewer 5.2.3
DisplayVersion : 5.2.3
ModifyPath : MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}
Vendor : RealVNC Ltd
Is this possible or am I going about this script entirely wrong? I haven't figured out a way to run this Invoke-Command for multiple parameters and still output the results in individual columns any other way.
This script works but takes forever for 100's of computers:
if (Test-Path C:\VNCInstalled.csv) {Remove-Item C:\VNCInstalled.csv}
if (Test-Path C:\Computers.txt) {Remove-Item C:\Computers.txt}
$DirSearcher = New-Object System.DirectoryServices.DirectorySearcher([adsi]'')
$DirSearcher.Filter = '(&(objectClass=Computer)(!(cn=*esx*)) (!(cn=*slng*)) (!(cn=*dcen*)) )'
$DirSearcher.FindAll().GetEnumerator() | sort-object { $_.Properties.name } `
| ForEach-Object { $_.Properties.name }`
| Out-File -FilePath C:\Computers.txt
Get-Content -Path c:\Computers.txt `
| ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} `
| select-object #{Name="ComputerName";Expression={$_.PSComputerName}},
Name,
#{Name="InstallLocation";Expression={$_.PackageCache}},
Vendor,
Version,
#{Name="GUID";Expression={$_.IdentifyingNumber}} `
| Sort-Object ComputerName `
| Export-CSV -path c:\VNCInstalled.csv -notype
Change all of your Select-Object commands to Select-Object
-ExpandProperty PropertyName, to discard the property name / column header.
This is the answer I gave three years ago and I think it was really a poor answer. Let me do a better job now.
Why your current code is slow
Your current code enumerates all machines from AD and puts them in a file called Computers.txt. Simple, and you do it fine.
Next up, your code performs this operation:
Get-Content -Path c:\Computers.txt |
ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} [...]
This can be summarized as 'For each computer, request the full Win32_product table, and then after that, filter down to apps named VNC.' This is HUGELY performance impacting and for a few reasons.
Even on a fast modern computer, querying Win32_Product will take 30 seconds or more, because it returns every application installed. (on a new VM for me it took more than a minute with just a handful of apps installed!)
Querying Win32_Product also has this fun quirk which makes it take even longer, quoted from MSDN Documentation on the Win32_Product Class
Warning Win32_Product is not query optimized. Queries such as "select * from Win32_Product where (name like 'Sniffer%')" require WMI to use the MSI provider to enumerate all of the installed products and then parse the full list sequentially to handle the “where” clause. This process also initiates a consistency check of packages installed, verifying and repairing the install. With an account with only user privileges, as the user account may not have access to quite a few locations, may cause delay in application launch and an event 11708 stating an installation failure. For more information, see KB Article 794524.
So to summarize, querying Win32_Product is slow, AND it also triggers a consistency chceck on every app, AND we also have this query written to retrieve every single app before filtering. These add up to a process which probably takes ~3 minutes per pc, and will operate serially (one after the other) and take forever.
How to fix it
Software info can be retrieved reliably in two places:
If you have SCCM/ConfigMgr installed on your devices, it adds the Win32_AddRemoveProgram WMI Class you can query, which is a super fast version of Win32_Product
If not, we can always retrieve info from the registry.
Here's a short snippet to get applications like VLC installed on a computer (I don't have VNC like you, so I'm making due)
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-object DisplayName -like "VLC*" |Select-Object DisplayName, DisplayVersion, Publisher, InstallDate,UninstallString
DisplayName : VLC media player
DisplayVersion : 3.0.8
Publisher : VideoLAN
InstallDate :
UninstallString : "C:\Program Files (x86)\VideoLAN\VLC\uninstall.exe"
This operation is much faster, only 400 MS or so. Sadly we cannot get much faster using the registry as it has a very weird PowerShell provider that doesn't implement the -Filter parameter, so we do have to retrieve all programs and then filter to our desired choice.
Updating your script to use this function instead
I took the liberty of rewriting your script to use this approach, and restructured it a bit for better readability.
$results = New-object System.Collections.ArrayList
$computers = Get-Content -Path c:\Computers.txt
foreach ($computer in $computers){
#get VNC entries from remote computers registry
$VNCKeys = Invoke-Command -ComputerName $computer -ScriptBlock {
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Where-object DisplayName -like "VNC V*" |
Select-Object DisplayName, DisplayVersion, Publisher, UninstallString, #{Name=‘ComputerName‘;Expression={$computer}}
}#end of remote command
if ($VNCKeys -ne $null){
forEach($VNCKey in $VNCKeys){
[void]$results.Add($VNCKey)
}
}
}
$results | Sort-Object ComputerName | Export-CSV -path c:\VNCInstalled.csv -NoTypeInformation
I want to write a PowerShell script to get all the print servers in a network.
I have used the following LDAP query, but it returns only servers with network printers attached to it. But not other print servers that have a remote printer attached to it.
Here's the code I used to get the print servers (But getting only the servers with n/w printers)
Import-Module ActiveDirectory
[array]$testarray = Get-ADObject -LDAPFilter "(&(&(&(uncName=*)(objectCategory=printQueue))))" -properties *|Sort-Object -Unique -Property servername |select servername
$testarray
You could try something like Get-WMIObject win32_printer | select name,local wrapped in a foreach-object loop like this:
$servers | ForEach-Object {
Get-WMIObject Win32_Printer -computername $_ | Where-Object {$_.local -like 'False'} | select Name,local,SystemName | format-table -a
}
The $servers can be what scoped to whatever servers you need to check.
Use Get-WMIObject Win32_Printer | select * to see what properties you want to report back on and include them in the | select Name,local,SystemName section of the script
You could use a combination of Get-ADComputer and Get-Printer to list all shared printer queues on computers joined to your domain:
Get-ADComputer -Filter * | % {
$computer = $_.Name
Get-Printer -Computer $computer | ? { $_.Shared } | select -Expand Name
}
This doesn't cover computers that aren't domain members, though.