function Get-AllServices { <# .SYNOPSIS Used to find all services on a system but with all the details that we might use Queries the registry to find all services that have at least an ImagePath, but filters out a majority of Windows Services (not all) .PARAMETER All Returns everything that wasn't filtered out for other reasons .PARAMETER Name Used to match names .PARAMETER Path Used to match paths .PARAMETER Fragment Used to search for any occurrence on path or name .PARAMETER Exact Used to specify matching an exact path or name #> [CmdletBinding(DefaultParameterSetName = 'All')] param ( [Parameter(ParameterSetName = 'All')] [switch]$All, [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [Alias('NameLike')] [Alias('NamePartial')] [Alias('NameFragment')] [string]$Name, [Parameter(Mandatory = $true, ParameterSetName = 'Path')] [Alias('PathLike')] [Alias('PathPartial')] [Alias('PathFragment')] [string]$Path, [Parameter(Mandatory = $true, ParameterSetName = 'Fragment')] [Alias('Query')] [string]$Fragment, [Parameter(Mandatory = $false, ParameterSetName = 'Name')] [Parameter(Mandatory = $false, ParameterSetName = 'Path')] [switch]$Exact, [switch]$RefreshCache ) begin { $logLead = (Get-LogLeadName) # Define the defaults for the ServiceController response to add some custom display information $defaultTypeName = 'ServiceObject' $defaultKeys = @('Name') $defaultDisplaySet = @('Name', 'RunAs', 'StartMode', 'ExePath') $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet) $defaultKeyPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultKeyPropertySet',[string[]]$defaultKeys) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet,$defaultKeyPropertySet) } process { $contains = $Name.IndexOf('*') -or $Path.IndexOf('*') -or $Fragment.IndexOf('*') if ($contains -and $Exact) { throw "$logLead : Can not search for an Exact match AND use wildcards" } $checkPath = ![string]::IsNullOrWhiteSpace($Path) $checkName = ![string]::IsNullOrWhiteSpace($Name) if (![string]::IsNullOrWhiteSpace($Fragment)) { $checkPath = $true $checkName = $true $Name = $Fragment $Path = $Fragment } $Name = $Name -Replace '\*','' $Path = $Path -Replace '\*','' if ($RefreshCache) { $global:get_allservices_servicesWithPaths = $null } if ($null -ne $global:get_allservices_servicesWithPaths) { $servicesWithPaths = $global:get_allservices_servicesWithPaths } else { $allServices = Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services\ # If there is no path property, we really don't care for the purposes of this function $servicesWithAnyPath = $allServices.Where({$_.Property -Contains 'ImagePath'}) $servicesWithPaths = $servicesWithAnyPath.Where({!$_.GetValue('ImagePath').StartsWith("\SystemRoot")}) $global:get_allservices_servicesWithPaths = $servicesWithPaths } # https://docs.microsoft.com/en-us/windows/win32/msi/serviceinstall-table $serviceType = @{0x1 = 'KernelDriver';0x2 = 'FileSystemDriver';0x4 = 'Adapter';0x8 = 'RecognizerDriver';0x10 = 'Win32OwnProcess';0x20 = 'Win32ShareProcess';0x100 = 'InteractiveProcess';} $startMode = ('Boot','System','Automatic','Manual','Disabled') $errorControl = ('Ignore','Normal','Severe','Critical') $services = @() foreach ($regService in $servicesWithPaths) { $serviceName = (Split-Path -Path $regService -Leaf) if ($checkName) { $checkMatch = $checkName -and ($serviceName.IndexOf($Name, [StringComparison]::OrdinalIgnoreCase) -gt -1) if ($checkName) { # Write-Host "$logLead : $checkMatch | $serviceName | $Name" } $exactMatch = $Exact -and ($serviceName -eq $Name) if ($Exact) { # Write-Host "$logLead : $exactMatch | $serviceName | $Name" } if ($checkMatch -or $exactMatch) { # We can continue, we found a matching name # Write-Host "$logLead : Found $serviceName matched" } else { continue } } $displayName = $regService.GetValue("DisplayName") if (![string]::IsNullOrWhiteSpace($displayName)) { if ($displayName.IndexOf('@') -eq 0) { if ($displayName.IndexOf(';') -gt -1) { $displayName = $displayName.Substring($displayName.LastIndexOf(';')) } }} else { $displayName = $serviceName } if ($displayName.IndexOf('@') -eq 0) { # Neat trick: Alkami doesn't care about these services _at all_ continue } $description = $regService.GetValue("Description") if (![string]::IsNullOrWhiteSpace($description)) { if ($description.IndexOf('@') -eq 0) { # Neat trick: Alkami _also_ doesn't care about these services _at all_ continue }} $imagePath = $regService.GetValue("ImagePath") $exePath = $imagePath if (![string]::IsNullOrWhiteSpace($imagePath)) { if ($imagePath.IndexOf('"') -gt -1) { $exePath = ($imagePath.Remove($imagePath.LastIndexOf(".exe",[StringComparison]::OrdinalIgnoreCase)) + ".exe").Replace('"','') }} $loginName = ($regService.GetValue("ObjectName"),'LocalSystem' -ne $null)[0] if ($checkPath) { $checkMatch = $checkPath -and ($exePath.IndexOf($Path, [StringComparison]::OrdinalIgnoreCase) -gt -1) if ($checkPath) { # Write-Host "$logLead : $checkMatch | $exePath | $Path" } $exactMatch = $Exact -and ($exePath -eq $Path) if ($Exact) { # Write-Host "$logLead : $exactMatch | $exePath | $Path" } if ($checkMatch -or $exactMatch) { # We can continue, we found a matching path # Write-Host "$logLead : Found $exePath matched" } else { continue } } $startModeValue = $regService.GetValue("Start") $obj = @{ Name = $serviceName StartMode = $startMode[$startModeValue] StartType = <#[System.ServiceProcess.ServiceStartMode]#>$startModeValue ErrorControl = $errorControl[$regService.GetValue("ErrorControl")] ImagePath = $imagePath ExePath = $exePath RunAs = $loginName DisplayName = $displayName Description = $description Dependencies = $regService.GetValue("Dependencies") ManagedServiceAccount = [System.BitConverter]::ToInt32(($regService.GetValue("ServiceAccountManaged"),@(0,0,0,0) -ne $null)[0],0) -eq 1 FailureActions = ConvertFrom-FailureActions -FailureActions $regService.GetValue("FailureActions") # FailureActionsOnNonCrashFailures https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actions_flag # If this member is TRUE and the service has configured failure actions, the failure actions are queued if the service process terminates without reporting a status of SERVICE_STOPPED or if it enters the SERVICE_STOPPED state but the dwWin32ExitCode member of the SERVICE_STATUS structure is not ERROR_SUCCESS (0). # If this member is FALSE and the service has configured failure actions, the failure actions are queued only if the service terminates without reporting a status of SERVICE_STOPPED. FailureActionsOnNonCrashFailures = $regService.GetValue("FailureActionsOnNonCrashFailures") -eq 1 FailureCommand = $regService.GetValue("FailureCommand") DelayedAutostart = $regService.GetValue("DelayedAutostart") AutoRun = $regService.GetValue("AutoRun") AutoRunAlwaysDisable = $regService.GetValue("AutoRunAlwaysDisable") AutorunsDisabled = $regService.GetValue("AutorunsDisabled") StateFlags = $regService.GetValue("StateFlags") Type = $serviceType[$regService.GetValue("Type")] # Where we find this key # Key = $regService # The properties this object has so we can get the data later for confirming we got everything we want # Property = $regService.Property } $serviceController = New-Object PSCustomObject -Property $obj $serviceController.PSObject.TypeNames.Insert(0,$defaultTypeName) $serviceController | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers $services += $serviceController } return $services } }