Saturday, May 11, 2024
HomePowershellUse PowerShell to seek out Home windows providers configured to run as...

Use PowerShell to seek out Home windows providers configured to run as one other person


Article Contents:

In the future at work my supervisor requested me to seek out all providers working below completely different credentials on a handful of a particular servers. The ask went one thing like this:

“Hey Mike, can you discover all of the providers configured to run as one other person on a particular group of servers?”

My supervisor hadn’t even completed his sentence, and I had already thought in my head, “No downside! I am going to crank that out in PowerShell in a mere couple of minutes!” The ask was benign, however as I quickly came upon, the method for locating the requested info was not as easy as I assumed. Let’s dive in and take a look at how I solved this request.

First, let’s evaluate the duty and break it down into related items:

“Discover all of the providers configured to run as one other person on a gaggle of servers.”

From that sentence, we will break that down to those separate duties to unravel:

  • question a gaggle of servers
  • discover all of the providers configured to run as one other person
  • return the outcomes
  • (implied however not stated) format the output of the outcomes in order that we will perceive the knowledge

Discovering Logon As or Run As info

The very first thing we’ll want to determine is a strategy to discover the “logon as” properties for providers on Home windows pc. Let’s begin by querying a service on my native pc to see what fields can be found. I am going to begin with a easy assist lookup:

PS C:Usersmkana> assist get-service

NAME
    Get-Service

SYNOPSIS
    Will get the providers on a neighborhood or distant pc.


SYNTAX
    Get-Service [-ComputerName <String[]>] [-DependentServices] -DisplayName <String[]> [-Exclude <String[]>]
    [-Include <String[]>] [-RequiredServices  [<CommonParameters>]

    Get-Service [-ComputerName <String[]>] [-DependentServices] [-Exclude <String[]>] [-Include <String[]>]
    [-InputObject <ServiceController[]>] [-RequiredServices] [<CommonParameters>]

    Get-Service [[-Name] <String[]>] [-ComputerName <String[]>] [-DependentServices] [-Exclude <String[]>]
        [-Include <String[]>] [-RequiredServices] [<CommonParameters>]

I see there are ComputerName and DisplayName fields which might be useful, however nothing that offers me ‘Logon As‘ or ‘Run As‘.
Let’s attempt piping get-service to get-member and see if there are extra properties out there to me. I can take a look at the MemberType column and see all of the properties out there.

PS C:Usersmkana> get-service LanmanServer | get-member
   TypeName: System.ServiceProcess.ServiceController
Identify                      MemberType    Definition
----                      ----------    ----------
Identify                      AliasProperty Identify = ServiceName
RequiredServices          AliasProperty RequiredServices = ServicesDependedOn
Disposed                  Occasion         System.EventHandler Disposed(System.Object, System.EventArgs)
Shut                     Technique        void Shut()
Proceed                  Technique        void Proceed()
CreateObjRef              Technique        System.Runtime.Remoting.ObjRef CreateObjRef(sort requestedType)
Dispose                   Technique        void Dispose(), void IDisposable.Dispose()
Equals                    Technique        bool Equals(System.Object obj)
ExecuteCommand            Technique        void ExecuteCommand(int command)
GetHashCode               Technique        int GetHashCode()
GetLifetimeService        Technique        System.Object GetLifetimeService()
GetType                   Technique        sort GetType()
InitializeLifetimeService Technique        System.Object InitializeLifetimeService()
Pause                     Technique        void Pause()
Refresh                   Technique        void Refresh()
Begin                     Technique        void Begin(), void Begin(string[] args)
Cease                      Technique        void Cease()
WaitForStatus             Technique        void WaitForStatus(System.ServiceProcess.ServiceControllerStatus desiredStatus), void WaitForStat
CanPauseAndContinue       Property      bool CanPauseAndContinue {get;}
CanShutdown               Property      bool CanShutdown {get;}
CanStop                   Property      bool CanStop {get;}
Container                 Property      System.ComponentModel.IContainer Container {get;}
DependentServices         Property      System.ServiceProcess.ServiceController[] DependentServices {get;}
DisplayName               Property      string DisplayName {get;set;}
MachineName               Property      string MachineName {get;set;}
ServiceHandle             Property      System.Runtime.InteropServices.SafeHandle ServiceHandle {get;}
ServiceName               Property      string ServiceName {get;set;}
ServicesDependedOn        Property      System.ServiceProcess.ServiceController[] ServicesDependedOn {get;}
ServiceType               Property      System.ServiceProcess.ServiceType ServiceType {get;}
Website                      Property      System.ComponentModel.ISite Website {get;set;}
StartType                 Property      System.ServiceProcess.ServiceStartMode StartType {get;}
Standing                    Property      System.ServiceProcess.ServiceControllerStatus Standing {get;}
ToString                  ScriptMethod  System.Object ToString();

As I look by means of the record, I’m on the lookout for properties that could be useful or a match. Nevertheless, I see nothing that can return the ‘Logon As‘ or ‘Run_As‘ info that I would like; that is an issue. I can use Get-Service to question the service info of a neighborhood or distant pc, however that cmdlet doesn’t return any ‘Logon As’ or ‘Run_As‘ info in any respect.

Here is an instance:

PS C:Usersmkana> get-service LanmanServer | Choose-Object *
Identify                : LanmanServer
RequiredServices    : {SamSS, Srv2}
CanPauseAndContinue : False
CanShutdown         : False
CanStop             : True
DisplayName         : Server
DependentServices   : {}
MachineName         : .
ServiceName         : LanmanServer
ServicesDependedOn  : {SamSS, Srv2}
ServiceHandle       :
Standing              : Working
ServiceType         : Win32OwnProcess, Win32ShareProcess
StartType           : Computerized
Website                :
Container           :

Houston, we now have an issue!

Looking out CIM Knowledge for properties

We’ll have to think about using another technique of retrieving the related info in an effort to full this request. Let’s examine what’s out there from WMI/CIM.

A be aware about CIM and WMI

💡

I really like CIM and it’s best to study to like it as properly. CIM and WMI are the identical factor, however you entry CIM utilizing the newer Get-CIMInstance cmdlet as a substitute of Get-WMIObject. It is the newer, extra fashionable model of WMI however makes use of PS remoting (aka WinRM) and falls again to WMI typically if CIM fails.

It is like one of the best of each worlds! Your community workforce will love CIM over WMI as a result of it makes use of commonplace ports as a substitute of a variety of ports like WMI makes use of! If you have not already transitioned to utilizing Get-CIMInstance when querying WMI, then you have to use Get-CIMInstance as your default technique, as a substitute of Get-WMIObject .
Get-WMIObject` does not work on newer variations of Home windows (Home windows 10, 11, Server2016, Server2019 & Server 2022)

I talk about the variations between CIM and WMI at size in my article on The right way to safe PowerShell Remoting in a Home windows Area .


The lookup I have to carry out for CIM is:

PS > Get-CIMInstance -Class Win32_Service -Filter "title="LanmanServer" " | Choose-Object *

I am going to clarify this syntax in only a second. However first, let’s take a look at the outcomes. I’m displaying you a display screen cap as a result of it is simpler to spotlight the knowledge I’m on the lookout for.

CIMInstance Example

Alright, now we’re getting someplace!

CIM offers us what we’d like, now we simply have to determine what fields we need to return then do some filtering. First, we have to resolve what data we need to return from CIM. I feel the next fields cowl the whole lot we’d like for this activity:

  • SystemName
  • Identify
  • Caption
  • StartMode
  • StartName
  • State

Your mileage could differ and you’ll select no matter fields you like, however for this downside, these fields fill the invoice.

Constructing a question with Get-CIMInstance

Now that we all know what we need to question, we have to work on some filters. Let’s question the fields we simply recognized and make these properties comprise the info we count on. Here is a fast check to see how that appears for one service question:

PS > Get-CIMInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " | 
    Choose-Object SystemName, Identify, Caption, StartMode, StartName, State | Kind-Object StartName

Systemname : DC02
Identify       : tapisrv
Caption    : Telephony
StartMode  : Handbook
StartName  : mkmkadmin
State      : Stopped

That appears like the whole lot I would like for my authentic ask. Let’s evaluate the place we’re at thus far.

We have now queried the knowledge we’d like, and we now have discovered the properties to question. Now, let’s determine the way to seek for providers configured to run as one other person. If we take a look at a typical PC, the overwhelming majority of providers run as one among three built-in accounts:

  • LocalSystem
  • NT AUTHORITYLocalService
  • NT AUTHORITYNetworkService

I can construct a filter to return accounts not configured as one of many three accounts listed above. Since we’re on the lookout for ANY account used to RUN AS, any account besides these three above can be legitimate outcomes.

To look CIM (or WMI) for any account besides the three above, we’d use the filter parameter and the syntax would appear like this:

 -filter " StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' "

The != is equal to NOT EQUAL TO and the assertion AND NOT StartName LIKE is similar as "StartName -notlike 'NT Authority%'" in conventional PowerShell code. We will interpret the search as “Discover all accounts which aren’t equal to the title LocalSystem and likewise don’t begin with NT Authority* “. In less complicated phrases, it interprets to “all accounts that aren’t one among three inbuilt we listed above”.

Let’s check that question to ensure it really works as we count on. So as to take action, I reconfigured the Telephony Service to run below my admin credentials.

telephony-screenshot

Let’s examine if our filter works as anticipated. If it does, we must always see the one service (Telephony Service) returned as the one service configured with an alternate Logon As credential.

Get-CIMInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " | 
    Choose-Object SystemName, Identify, Caption, StartMode, StartName, State | Kind-Object StartName

SystemName : DC02
Identify       : tapisrv
Caption    : Telephony
StartMode  : Handbook
StartName  : mkmkadmin
State      : Stopped

Success!!!

Now that we now have a working search standards, we will run this question in opposition to the servers. We must always get an inventory of providers that match our standards of working with an alternate Logon As credential.

I’ve three servers I’m going to question: DC01, DC02, and AZBuild01. There are a number of strategies that may join to every server and carry out the question. I might use ForEach-Object and join utilizing Get-CIMInstance and add the -ComputerName parameter. For this activity, I desire to make use of PowerShell remoting (aka WinRM).

Utilizing PowerShell Remoting to connect with computer systems

The syntax for PowerShell Remoting is useless easy to make use of. The cmdlet we’ll use is Invoke-Command (or ICM alias). We might have used Enter-PSSession to connect with every server interactively. As a substitute, Invoke-Command will hook up with as much as 32 computer systems in a single shot within the background. It’s going to retrieve the knowledge I ask for and return the info to me in a formatted output.

💡

Should you’re not aware of the way to use Invoke-Command, go to my Bounce Begin: PowerShell Remoting Article for a refresher on all of the completely different strategies out there to attach and the fitting syntax to make use of.

The syntax is the cmdlet title, the pc title after which the script block. The script block is the place you place the precise lookup you wish to have carried out on every pc.

Invoke-Command -ComputerName MyServerNames -ScriptBlock {Insert your code right here}

Should you recall out earlier, lookup through CIMInstance was:

Get-CIMInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " | 
    choose systemname, title, caption, startmode, startname, State | kind startname

the syntax utilizing Invoke-Command can be:

Invoke-Command "DC01", "DC02", "AzBuild01" -Scriptblock  kind startname

The output we get is:

Invoke-Command-Results-01

Outcomes return rapidly as a result of we’re utilizing -filter as a substitute of where-object. The distinction is that filter sends the code to the distant pc. The pc does the lookup and finds the matches and solely sends again the matches. If I used where-object, then all of the providers from the distant machine can be despatched again after which after all of the computer systems have responded with all their providers, where-object would then filter out the outcomes on my native pc.

I’m solely contacting three computer systems, so the distinction in pace is just not noticeable. But when I despatched this to 100 or 200 computer systems, then the distinction between utilizing -filter or The place-object turns into a giant deal. The distinction is often seconds vs ten’s of seconds for The place-object. You additionally need to use -filter as a substitute of where-object until -filter is just not an possibility that’s out there for a cmdlet.

Formatting output

We have now fulfilled the request, however I see two issues. The primary downside is that the output has some fields that my supervisor is not going to know what they imply. The fields Identify and Caption are ambiguous and want higher clarification. We will repair through the use of expressions to customise the names.

Invoke-Command "DC01", "DC02", "AzBuild01" -Scriptblock {
    Get-CIMInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " |
        Choose-Object SystemName, @{ Identify="ServiceName";  Expression = {$_.Identify}},
            @{ Identify="Service DisplayName";  Expression = {$_.Caption}}, StartMode, StartName, State | kind StartName}

Invoke-Command-Results-02

That is higher. Now once I ship the outcomes to my supervisor, he’ll perceive the output and never should ask me questions.

Simplifying our code

The second downside I see in my code is that it is now a monster command that’s exhausting for others to grasp. If I save my code and go it to a junior admin or a PowerShell novice, they might wrestle making an attempt to grasp the code. We solved the issue, however we must always attempt to enhance the readability for others. The answer for readability is variables.

If we take the assorted components of the code and save them to variables, then the cmdlet will get a lot simpler to learn. We will assign variables for the server names and the 2 expressions.

Let’s begin with the server names:

$Servers = "DC01", "DC02", "AzBuild01"

and we will additionally do the identical for expressions:

$ServiceName =  @{ Identify="ServiceName"; Expression = {$_.Identify}}
$ServiceDisplayname = @{ Identify="Service DisplayName";  Expression = {$_.Caption}}

Now we will substitute these values in our code:

$Servers = "DC01", "DC02", "AzBuild01"
$ServiceName =  @{ Identify="ServiceName"; Expression = {$_.Identify}}
$ServiceDisplayname = @{ Identify="Service DisplayName";  Expression = {$_.Caption}}

Invoke-Command $servers -ScriptBlock {
    Get-CimInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " } |
        Choose-Object SystemName, $ServiceName, $ServiceDisplayname, StartMode, StartName, State

Invoke-Command Results 03)

Our code is far more readable than earlier and once we hand it over to another person, they’ll have a significantly better probability of understanding what this code does.

The final change I’ll make is to alter the output to record as a desk.

$Servers = "DC01", "DC02", "AzBuild01"
$ServiceName =  @{ Identify="ServiceName"; Expression = {$_.Identify}}
$ServiceDisplayname = @{ Identify="Service DisplayName";  Expression = {$_.Caption}}

Invoke-Command $servers -ScriptBlock {
        Get-CimInstance -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " } | 
            Choose-Object SystemName, $ServiceName, $ServiceDisplayname, StartMode, StartName, State | format-table -autosize

Invoke Command Results 04

Conclusion

I hoped you loved this look into how I tackled a real-world downside. We began with a activity and located the pertinent info wanted to meet the request. After we discovered what and the way to question the knowledge, we labored on the way to repeat that question in opposition to a number of computer systems. Then we labored on formatting the output to one thing that’s human readable for non admin workers to work with. We might nonetheless take these outcomes and export them to a CSV or higher but, an Excel file, that are commonplace workplace format nearly anybody can open and eat.

Thanks for studying, I might like to know what you suppose. Go away me a message within the remark part on the backside of the web page.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments