There has to be a better way
$server = (Get-ADComputer -Filter * -Properties *).name
foreach ($s in $server)
{
Get-WmiObject Win32_Service -filter 'STARTNAME LIKE "%serviceaccount%"' -computername $s
}
I want to search all servers on the domain for a service account. The above kind of does what I'm looking for but it doesnt return what server the services account was found on. Thanks in advance.
here's what i meant about using Get-Member to find the object properties that would give you the info you want. [grin]
this could be sped up considerably by giving the G-WO call a list of systems. i wasn't ready to code that just now. lazy ... [blush]
what it does ...
sets the account to look for
i only have the LocalSystem and NetworkService accounts listed on my services. [grin]
sets the computer list to search
you will likely use Get-ADComputer. make sure to either use the property name in the loop OR to make your query return only the actual name value.
i only have one system, so my list is 3 different ways to get to the same computer.
loops thru the systems
call G-WO to get the service[s] that use the target account
builds a [PSCustomObect] with the wanted properties
sends that to the $Result collection
shows that on screen
the code ...
$TargetAccount = 'LocalSystem'
$ComputerList = #(
'LocalHost'
'127.0.0.1'
$env:COMPUTERNAME
)
$Result = foreach ($CL_Item in $ComputerList)
{
# i didn't want a gazillion services, so this uses array notation to grab the 1st item
# if you want all the items, remove the trailing "[0]"
$GWMI_Result = #(Get-WmiObject -Class Win32_Service -Filter "STARTNAME LIKE '%$TargetAccount%'" -ComputerName $CL_Item)[0]
[PSCustomObject]#{
ComputerName = $GWMI_Result.SystemName
AccountName = $GWMI_Result.StartName
ServiceName = $GWMI_Result.Name
}
}
$Result
output ...
ComputerName AccountName ServiceName
------------ ----------- -----------
MySysName LocalSystem AMD External Events Utility
MySysName LocalSystem AMD External Events Utility
MySysName LocalSystem AMD External Events Utility
Related
New-PSSession wants a -ConnectionURI of an explict Exchange server. I don't want to hardcode a name in the script (we have 32 servers), and furthermore I want it to select an exchange that is in the same datacenter.
I want a solution similar to Get-ADDomainController -Discover -GetClosestSite But it seems I'm hoping for too much.
I suppose I can pull the members of cn=Exchange Install Domain Servers and do some site dependent ranking on them.
Looking for best practices.
Update Edit: 9/26 I have achieved a solution. It may be site specific, but I'll share below in an answer to show the final code. The answer provided by postanote provided pointers that helped me move forward.
There is no official documented best practices for PowerShell in general (there are too many variables in the mix, but some have put their thoughts in the topic , for example, this one - https://github.com/PoshCode/PowerShellPracticeAndStyle ) or for what you are asking for from Microsoft.
As for you point here:
I suppose I can pull the members of cn=Exchange Install Domain Servers
and do some site dependent ranking on them.
This is not something new, or tricky, so you can do this.
I have code in my personal library that I use that does this as well as for other doing resources, so I never have to hardcode server names for Exchange, SQL, DC, etc.
There are several blogs (that have been out there for a while now) on the topic with sample code to use as is or tweak as needed, which is why I asked what you've searched for.
One of those blog examples of how to do this is here:
https://use-powershell.blogspot.com/2012/12/find-exchange-servers-in-domain.html
The examples provided:
an active directory user with mailbox will have 2 attributes (msExchHomeServerName and homemdb) that will contain the name of the
mailbox server that has his mailbox - once conected to one server you
can use exchange console to find the rest of them;
Get-ADUser samaccountname -Properties msExchHomeServerName, homemdb |Select-Object msExchHomeServerName, homemdb |Format-List
active directory computer type objects contain "exchange" word in servicePrincipalName attribute; you can use only your
organizational unit that contain your servers if you have one to
narrow your search:
Get-ADComputer -Filter * -SearchBase 'OU= SERVERS, DC=domain_name,DC=net' -Properties * | Where-Object {$_.serviceprincipalname -like '*exchange*'} |select-object name
active directory configuration partition contain information about exchange servers in domain; you can search for objects of class
msExchExchangeServer:
Get-ADObject -LDAPFilter "(objectClass=msExchExchangeServer)" –SearchBase "CN=Configuration,DC=domainname,DC=net" | Select-Object name
or you can list all objects from "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=INTERNAL,CN=Microsoft
Exchange,CN=Services,CN=Configuration,DC=domainname,DC=net" using
powershell or ADSI Edit console;
Get-ADObject -Filter * -SearchBase "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=INTERNAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domainname,DC=net" -SearchScope onelevel
Or this post:
https://social.technet.microsoft.com/Forums/ie/en-US/94d89161-9dfb-48fc-b307-2f0e1320c9dc/how-to-find-file-servers-and-exchange-servers-in-ad-
Example:
dsquery * "cn=Configuration,dc=MyDomain,dc=com" -Filter "(objectCategory=msExchExchangeServer)"
Or if you are really trying to get an Exchange server in given site, then this to already exists. See this GitHub source:
https://github.com/mikepfeiffer/PowerShell/blob/master/Get-ExchangeServerInSite.ps1
The sample provided is:
function Get-ExchangeServerInSite {
$ADSite = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]
$siteDN = $ADSite::GetComputerSite().GetDirectoryEntry().distinguishedName
$configNC=([ADSI]"LDAP://RootDse").configurationNamingContext
$search = new-object DirectoryServices.DirectorySearcher([ADSI]"LDAP://$configNC")
$objectClass = "objectClass=msExchExchangeServer"
$version = "versionNumber>=1937801568"
$site = "msExchServerSite=$siteDN"
$search.Filter = "(&($objectClass)($version)($site))"
$search.PageSize=1000
[void] $search.PropertiesToLoad.Add("name")
[void] $search.PropertiesToLoad.Add("msexchcurrentserverroles")
[void] $search.PropertiesToLoad.Add("networkaddress")
$search.FindAll() | %{
New-Object PSObject -Property #{
Name = $_.Properties.name[0]
FQDN = $_.Properties.networkaddress |
%{if ($_ -match "ncacn_ip_tcp") {$_.split(":")[1]}}
Roles = $_.Properties.msexchcurrentserverroles[0]
}
}
}
I'm accepting postanote's answer as being most helpful.
In the end, I created a solution that may be site and Exchange install specific, but it does illustrate another technique.
My constraints that were different than most other solutions I found and include; that the given user did not currently have a mailbox on the system. Hence the code beyond this snippet will gather some data, then select a specific database.
I discovered that the Exchange server version we have installed, creates some records in "CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects," In particular, the Members attribute has a list of servers.
I extracted that list, sorted it by Site (local to front), and then resolved the FQDN to form a URI to connect.
Function Connect-Exchange {
# Find servers list, then sort by name given what site we are running in: SITE1 = Ascending , SITE2 = Descending
$ADSite = (Get-ADDomainController).Site
if ($ADSite -like "SITE1*") { $descOrder = $true } else { $descOrder = $false }
$exchSession = $null
$ExchServersDN = "CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects,DC=example,DC=com”
$ExchServers = (Get-ADObject -Identity $($ExchServersDN) -Properties Member).Member | Sort-Object -Descending:$descOrder
# Iterate through Exchange server list until connection succeeds
$i = 0;
while ((-Not $exchSession) -and ($i -lt $ExchServers.Count)) {
$ExchServerURI = "http://" + (Get-ADObject -Identity $ExchServers[$i] -Properties dNSHostName).dnsHostName + "/Powershell"
$exchSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI $ExchServerURI -ErrorAction SilentlyContinue
If (-Not $exchSession) { $i++ }
else {
Import-PSSession $exchSession -DisableNameChecking | Out-Null
}
}
return $exchSession
}
I've tried a variety of iterations of this and gotten a range of errors. I'm trying to get a a list of installed drivers off from a list of computers. None of the ways I've tried in PowerShell have piped the information into a csv. Here's the current iteration of the script.
#Load Active Directory
Import-Module activedirectory
#Load list of computers
$results = #()
$Computer = Get-Content -path 'C:\ScriptResources\computers.txt'
#Check each computer in the list
foreach($ComputerName in $Computer)
{
$results += Get-ADComputer -Filter " Name -Like '*$ComputerName*' " | Get-PrinterDriver; Start-Sleep -milliseconds 500
}
#Export to CSV file
$results | export-csv 'C:\ScriptResults\InstalledPrinters.csv'
I've also used it with just the Get-Printer command and got the following error.
Get-Printer : No MSFT_Printer objects found with property 'Name' equal to 'Redacted'. Verify the value of the
property and retry.
Depending what I've fed the $Computer file I'll get different errors. I've also gotten the RPC server is unavailable and Error Spooler Service Not Running. I have domain wide privileges and I checked the print spooler service and it is running.
The reason I think this is odd is that I have .bat tool that I use that gets printer info from a singular host and I don't run into any issues. The reason I'm trying to put this in PowerShell is because 1) I want to do the whole domain and 2) PowerShell formats its outputs in a more useable fashion.
wmic /node:%ComputerIP% path win32_printer get deviceid, drivername, portname
Additionally, I've also tried the following in the $results function of the script
$results += Get-WmiObject -class Win32_printer -ComputerName name, systemName, shareName
This didn't give errors. What it did instead is that for each computer in the list of computers it checked the computer I was running the script from for its printers and output on each line which printers were installed on my computer.
I'm at a loss and any help would be appreciated. Thanks!
Just so this is closed out. Vivek's answer ended up working.
$results += Get-WmiObject -class Win32_printer -ComputerName $Computer | Select name, systemName, shareName
The RPC issue I was getting was that the list of computers were all turned off for some reason (remote site + different time zone + doing the testing during second shift). Normally, everything remains on though. So that was just an anomaly.
Thanks for the help!
I'm trying out a script to go grab installed software on servers remotely. Problem is I want it to output certain attribs including the computer name but I can't seem to figure out how to get the name inserted.
Here is what I have so far...
$servers = Get-QADComputer -SearchRoot "OU=servers,OU=mydomain:-),DC=COM" | Select Name
...which works fine of course. Then...
$servers | % {Get-WMIObject -Class Win32Reg_AddREmovePrograms} | select Displayname,Version,InstallDate,PSComputerName
... which provides the full list of software installed on all servers in that OU but the PSComputerName becomes MY COMPUTER (the computer I run the query from - not the computername of the system being queried). The goal is to have the servername the software is installed on on each line item of software. I've asked professor Google and don't seem to see anything helpful (or anything that I understand anyway).
Hope this makes sense. semi-amateur PS script writer so hopefully this is easy for you guys. Thanks in advance for your help
Your command:
Get-WMIObject -Class Win32Reg_AddREmovePrograms
Does not specify computer to query, so it just query computer command being executed on. Thus PSComputerName display MY COMPUTER, as MY COMPUTER is computer being queried. You have to specify -ComputerName parameter to Get-WMIObject cmdlet to query specific computer. And -ComputerName parameter accept array of computer names, so you can put array of computer names to it instead of using ForEach-Object cmdlet and query one computer at time.
Since the object returned from the WMI call doesn't contain the computer you made the request on, you need to include it yourself from include your ForEach-Object (%) block. You could use Add-Member to add it yourself, then do your Select-Object outside like you're doing now:
$servers | % {
Get-WMIObject -Class Win32Reg_AddREmovePrograms -ComputerName $_ |
Add-Member -MemberType NoteProperty -Name ComputerName -Value $_ -PassThru
} | select Displayname,Version,InstallDate,ComputerName
Another way is to move the Select-Object to inside the block and do it within there, by creating a new property on the fly with a hashtable:
$servers | % {
Get-WMIObject -Class Win32Reg_AddREmovePrograms -computername $_ |
Select-Object Displayname,Version,InstallDate,#{Name='ComputerName';Expression={$_}}
}
I know that I can use PowerShell to check service status on multiple services. For example with something like this:
Get-Service -ComputerName server-a, server-b, server-c -Name MyService |
Select Name, MachineName, Status
Can somebody advice how I can modify this so that:
- Enumerate large number of servers like an array or somehow else so that make it more readable than if I put large number of servers in one line.
- Use a wildcard in service name parameter, e.g. "MYSERVICE*"
You can put your servers in a text file such as SERVERS.TXT :
Server-a
Server-b
Server-c
...
And use :
$servers = get-content SERVERS.TXT
Get-Service -ComputerName $servers -Name MyService | Select Name, MachineName, Status
You can do the same for services.
To answer your second question first, the -Name parameter of the Get-Service cmdlet supports wildcards, so you can simply do this to check several services with similar names:
Get-Service -Computer 'server-a', 'server-b', 'server-c' -Name MyService* |
select Name, MachineName, Status
The -Computer parameter accepts an array of strings, so you can read the server list from a file (containing one hostname per line), as JPBlanc suggested:
$computers = Get-Content 'C:\path\to\serverlist.txt'
Get-Service -Computer $computers -Name MyService* | ...
This is probably the best choice, as it separates data from code.
However, there are situations where it's more practical to keep data and code in the same file (e.g. when you move the script around a lot). In a situation like that you can define an array spanning multiple lines like this:
$computers = 'server-a',
'server-b',
'server-c',
...
Get-Service -Computer $computers -Name MyService* | ...
I have the following code pulling from my exchange server 2003.
connect-qadservice -service 'localhost'
foreach ($server in $exchangeservers)
{
$AllUsers += get-wmiobject -class Exchange_Mailbox -namespace Root\MicrosoftExchangeV2 -computername $server| select servername,storagegroupname, storename,mailboxdisplayname,totalitems,size, DeletedMessageSizeExtended, legacyDN, datediscoveredabsentInDS
}
$exchngver = "2003"
foreach ($user in $AllUsers)
{
$obj = new-object psObject
$office = get-qaduser -Identity $user.legacyDN | select office, description
}
disconnect-qadservice
and it doesn't grab all the mailbox stores on the server. Any idea why or what might be causing this?
thanks in advance
NOTE: IT seems to grab all the mailbox stores except for 1 in the 2nd storage group. I have no idea why this is... The funny thing is my vbscript grabs all the mailbox stores using the same namespace and class just fine.
So to start simple, does it come back correct before you unroll & start using the quest stuff?
Do you get the right number from:
(get-wmiobject -class Exchange_Mailbox -namespace Root\MicrosoftExchangeV2 -computername srv02).count
Have you checked permissions on the Stores/SGs?
Couple of things (not sure they are the cause (#1)):
you are looping over $exchangeservers but don't use $server in -computerName (there's a fixed "srv02" server name).
I would move the connect-qadservice -service 'localhost' out of the foreach servers loop (You call it for each server in exchangeservers).
You are calling get-qaduser twice ($tmp and $office) to get the user office and description, you can do it in one call ($tmo is redundant):