Start All Automatic Services on Windows

Current version:

# startAllAutoServices.ps1
# version 0.0.2
# Description:
#   - This script to scan a list of Windows machines and start any non-running services that have been set to automatically start,
#   - It will skip a list of service names as defined by the user
#   - Multi-threading enabled
# Requirements:
#   - Invoke function with Administrator privileges

$computerNames=@(
    'server1','server2'
)
$skipServices=@('gupdate','MapsBroker','RemoteRegistry','sppsvc','WbioSrvc','twdservice','ShellHWDetection','edgeupdate')
$credentials=$null # Assuming session context of SysAdmin

function startAllAutoServices{
    param(
        [string[]]$computerNames=$env:computername,
        $credentials=$null,
        [string[]]$skipServices=@('gupdate','MapsBroker','RemoteRegistry','sppsvc','WbioSrvc','MSCRMAsyncService$maintenance','twdservice')
        )    
    function startAutoServices{
        param (
            $computername=$env:computername,
            $skipServices=@('gupdate','MapsBroker','RemoteRegistry','sppsvc','WbioSrvc','MSCRMAsyncService$maintenance','twdservice')
            )
        # Start all non-running Auto-Start services
        $filterString=($skipServices|%{"AND name!='$_' "}) -join ''
        $timeZoneName=[System.TimeZoneInfo]::Local.StandardName
        $abbreviatedZoneName=if($timeZoneName -match ' '){[regex]::replace($timeZoneName,'([A-Z])\w+\s*', '$1')}else{$timeZoneName}
        $timeStampFormat="yyyy-MM-dd HH:mm:ss $abbreviatedZoneName"
        $timeStamp=[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([datetime]::UtcNow,$timeZoneName).ToString($timeStampFormat)
        $nonRunningServices=Get-WmiObject win32_service -ComputerName $computerName -Filter "startmode='auto' AND state!='running' $filterString"
        #$nonRunningServices=(Get-WmiObject -Query "select DisplayName, State from Win32_Service").Where( {$_.State -eq 'Stopped'})
        if($nonRunningServices){
            $null=$nonRunningServices|Invoke-WmiMethod -Name StartService
            # Detect non-running services
            $failedServices=(Get-WmiObject win32_service -ComputerName $computerName -Filter "startmode = 'auto' AND state != 'running' $filterString").Name #|select -expand Name
            if($failedServices){
                write-warning "$computerName`: services with 'stopped' status $failedServices"
            }else{
                write-host "$computerName`: all auto-start services are running." -ForegroundColor Green
            }
            return [pscustomobject]@{
                timeStamp=$timeStamp
                computername=$computerName
                stoppedServices=($nonRunningServices.Name|out-string).trim()
                failedToStart=if($failedServices){($failedServices|out-string).trim()}else{'None'}
            }
        }else{
            write-host "$computerName all auto-start services are running." -ForegroundColor Green
            return [pscustomobject]@{
                timeStamp=$timeStamp
                computername=$computerName
                nonRunningServices='none'
                failedToStart='none'
            }
        }
    }

    function testPort($remoteComputer=$env:computername,$port=135,$protocol='TCP'){
        function includePortQry{
            if (!(Get-Command portqry.exe -ErrorAction SilentlyContinue)){
                if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))}
                choco install portqry -y
                if (Get-Command portqry.exe -ErrorAction SilentlyContinue){return $true}else{return $false}
            }else{
                return $true
            }
        }
        $portQryExists=includePortQry
        if($portQryExists){
            $reachable=if($port -ne 135){                
                $command={
                    param($remoteComputer,$protocol,$port)
                    $command="portqry -n $remoteComputer -p $protocol -e $port|find ': LISTENING'"
                    invoke-expression $command
                }
                $jobId=(Start-Job $command -Args $remoteComputer,$protocol,$port).Id
                $startCount=0;$waitSeconds=5
                do{
                    $jobStatus=(get-job -id $jobId).State
                    if($jobStatus -eq 'Completed'){
                        $jobResult=receive-job $jobId
                    }else{
                        if($startCount++ -eq $waitSeconds){
                            $jobStatus='Completed'
                            $null=remove-job -id $jobId -force
                            $jobResult=$false
                        }else{
                            sleep 1
                        }                        
                    }
                }until($jobStatus -eq 'Completed')
                [bool]($jobResult)
            }else{
                [bool](portqry -n $remoteComputer -p $protocol -e $port|find 'Total endpoints found: ')
            }       
            write-host "$remoteComputer $port/$protocol`: $reachable"
        }else{
            write-warning "$env:computername doesn't have portqry. Now switching to unreliable porting testing methods"
            $reachable=test-netconnection $remoteComputer -port $port -informationlevel Quiet
            write-host "$remoteComputer $port/TCP: $reachable"
        }
        return $reachable
    }

    $results=@()
    foreach ($computerName in $computerNames){
        $rpcAvailable=testPort $computerName 135 'tcp'
        $winRmAvailable=if($rpcAvailable){$false}else{testPort $computerName 5985 'tcp'}
        if($rpcAvailable){
            $results+=startAutoServices $computername $skipServices
        }elseif($winRmAvailable){
            $sessionTimeout=New-PSSessionOption -OpenTimeout 120000 # 2 minutes
            $sessionIncludePort=New-PSSessionOption -IncludePortInSPN -OpenTimeout 120000
            $session=if($credentials){
                    try{
                        New-PSSession -ComputerName $computername -Credential $credentials -ea Stop -SessionOption $sessionTimeout
                    }catch{
                        New-PSSession -ComputerName $computername -Credential $credentials -SessionOption $sessionIncludePort
                    }
                }else{
                    try{
                        New-PSSession -ComputerName $computername -ea Stop -SessionOption $sessionTimeout
                    }catch{
                        New-PSSession -ComputerName $computername -SessionOption $sessionIncludePort
                    }
                }        
            if($session.state -eq 'Opened'){
                $result=invoke-command -Session $session -ScriptBlock{
                    param ($startAutoServices,$skipServices)
                    [scriptblock]::create($startAutoServices).invoke($skipServices)
                    } -args ${function:startAutoServices},$skipServices|select-object timeStamp,computername,StoppedServices,failedToStart     
                $results+=$result
                Remove-PSSession $session
            }else{
                write-warning "$env:computername cannnot connect to $computername via WinRM"
                $timeZoneName=[System.TimeZoneInfo]::Local.StandardName
                $abbreviatedZoneName=if($timeZoneName -match ' '){[regex]::replace($timeZoneName,'([A-Z])\w+\s*', '$1')}else{$timeZoneName}
                $timeStampFormat="yyyy-MM-dd HH:mm:ss $abbreviatedZoneName"
                $timeStamp=[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([datetime]::UtcNow,$timeZoneName).ToString($timeStampFormat)
                $results+=[pscustomobject]@{
                    timeStamp=$timeStamp
                    computerName=$computerName
                    stoppedServices='UnableToConnect'
                    failedToStart='UnableToConnect'
                    }
            }
        }else{
            write-warning "$env:computername is unable to connect to $computerName"
            write-host "Unable to connect to $computerName" -ForegroundColor Yellow
            $timeZoneName=[System.TimeZoneInfo]::Local.StandardName
            $abbreviatedZoneName=if($timeZoneName -match ' '){[regex]::replace($timeZoneName,'([A-Z])\w+\s*', '$1')}else{$timeZoneName}
            $timeStampFormat="yyyy-MM-dd HH:mm:ss $abbreviatedZoneName"
            $timeStamp=[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([datetime]::UtcNow,$timeZoneName).ToString($timeStampFormat)
            $results+=[pscustomobject]@{
                timeStamp=$timeStamp
                computername=$computerName
                nonRunningServices='UnableToConnect'
                failedToStart='UnableToConnect'
                }
        }
    }
    return $results
}

# $results=startAllAutoServices $computerNames $skipServices
# $results|?{$_.computername}|ft -wrap

function checkServices{
    param(
        $computerNames,
        $credentials,
        $skipServices,
        $maxMinutesPerJob=10,
        $verbose=$false
    )
    $timer=[System.Diagnostics.Stopwatch]::StartNew()
    $jobResults=@()
    $lineBreak=60
    $dotCount=0
    $minute=0
    $processorsCount=(Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
    $cpuLoad=(Get-WmiObject win32_processor|Measure-Object -property LoadPercentage -Average).Average
    $maxSimultaneousJobs=if($cpuLoad -gt 90){$processorsCount}else{($processorsCount*2)-1} # dynamically limiting concurrent jobs basing on available CPU cores
    write-host "CPU load detected as: $cpuLoad`%`r`nSetting concurrent jobs max count to be $maxSimultaneousJobs"
    $jobtimer=@{}
    foreach($computerName in $computerNames){
        $thisIterationCompleted=$false
        do {
            $jobsCount=(Get-Job -State 'Running').Count
            if ($jobsCount -lt $maxSimultaneousJobs){            
                if($verbose){write-host "Initiating job for $computerName"}
                $job=Start-Job -name $computerName -ScriptBlock {
                    param($startAllAutoServices,$computerName,$credentials,$skipServices)
                    [scriptblock]::create($startAllAutoServices).invoke($computerName,$credentials,$skipServices)
                } -Args ${function:startAllAutoServices},$computerName,$credentials,$skipServices
                $jobtimer[$job.Id] = [System.Diagnostics.Stopwatch]::startnew()
                $thisIterationCompleted=$true
            }else{
                if($verbose){
                    if($dotCount++ -lt $lineBreak){
                        write-host '.' -NoNewline
                    }else{
                        $minute++
                        write-host "`r`n$minute`t:" -ForegroundColor Yellow -NoNewline
                        $dotCount=0
                        }
                }
                sleep -seconds 1
            }
            $expiredJobs=$jobtimer.GetEnumerator()|?{$_.value.elapsed.totalminutes -ge $maxMinutesPerJob}
            if($expiredJobs){
                stop-job -id $expiredJobs.Name
                $expiredJobs.Name|%{$jobTimer.Remove($_)}
            }
        }until ($thisIterationCompleted)
    }
    $totalJobsCount=(get-job).count
    $processedCount=0
    while($processedCount -lt $totalJobsCount){
        $completedJobs=get-job|?{$_.State -eq 'Completed'}
        $stoppedJobs=get-job|?{$_.State -eq 'Stopped'}
        $expiredJobs=$jobtimer.GetEnumerator()|?{$_.value.elapsed.totalminutes -ge $maxMinutesPerJob}
        if($expiredJobs){
            stop-job -id $expiredJobs.Name
            $expiredJobs.Name|%{$jobTimer.Remove($_)}
        }
        if($completedJobs){
            foreach ($job in $completedJobs){
                $computer=$job.Name
                if($verbose){
                    write-host "`r`n===================================================`r`n$computer job COMPLETED with these messages:`r`n===================================================`r`n"
                }
                $jobResult=receive-job -id $job.id
                $jobResults+=,$jobResult
                remove-job -id $job.id -force
                $processedCount++
            }
        }
        if($stoppedJobs){
            foreach ($job in $stoppedJobs){
                $computer=$job.Name
                if($verbose){
                    write-host "`r`n===================================================`r`n$computer job STOPPED with these messages:`r`n===================================================`r`n" -ForegroundColor Red
                }
                $jobResult=receive-job -id $job.id
                # $jobResults+=,$jobResult
                $timeZoneName=[System.TimeZoneInfo]::Local.StandardName
                $abbreviatedZoneName=if($timeZoneName -match ' '){[regex]::replace($timeZoneName,'([A-Z])\w+\s*', '$1')}else{$timeZoneName}
                $timeStampFormat="yyyy-MM-dd HH:mm:ss $abbreviatedZoneName"
                $timeStamp=[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([datetime]::UtcNow,$timeZoneName).ToString($timeStampFormat)
                $jobResult=[pscustomobject]@{
                    timeStamp=$timeStamp
                    computerName=$computer
                    stoppedServices='serverTimeout'
                    failedToStart='Unknown'
                    }
                $jobResults+=,$jobResult
                remove-job -id $job.id -force
                $processedCount++
            }
        }
    }
    $minutesElapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
    $timer.stop()
    write-host "$($computerNames.count) computers were processed in $minutesElapsed minutes."
    return $jobResults #|select -property * -excludeproperty RunspaceId
}

$results=checkServices $computerNames $credentials $skipServices|select-object -Property timeStamp,computername,stoppedServices,failedToStart
$results|ft -wrap

Older version:

# startAllAutoServices.ps1
# This script to scan a list of Windows machines and start any non-running services that have been set to automatically start,
# It will skip a list of service names as defined by the user
# Requirement:
# Invoke function with Administrator privileges

$computerNames='sherver0001','sherver1000'
$skipServices=@('gupdate','MapsBroker','RemoteRegistry','sppsvc','WbioSrvc')

function startAllAutoServices{
    param(
        [string[]]$computerNames=$env:computername,
        [string[]]$skipServices=@('gupdate','MapsBroker','RemoteRegistry','sppsvc','WbioSrvc')
        )
    
    $results=@()
    foreach ($computerName in $computerNames){
        # Start all non-running Auto-Start services
        # $exceptions=($skipServices|%{"'$_'"}) -join ','
        $filterString=($skipServices|%{"AND name!='$_' "}) -join ''
        $nonRunningServices=Get-WmiObject win32_service -ComputerName $computerName -Filter "startmode='auto' AND state!='running' $filterString"
        if($nonRunningServices){
            $nonRunningServices|Invoke-WmiMethod -Name StartService
            # Detect non-running services
            $failedServices = Get-WmiObject win32_service -ComputerName $computerName -Filter "startmode = 'auto' AND state != 'running' $filterString" | select -expand Name
            if($failedServices){
                write-warning "$computerName`: services with 'stopped' status $failedServices"
            }else{
                write-host "$computerName`: all auto-start services are running." -ForegroundColor Green
            }
            $results+=[pscustomobject]@{
                computername=$computerName
                nonRunningServices=($nonRunningServices.Name|out-string).trim()
                failedToStart=($failedServices|out-string).trim()
            }
        }else{
            write-host "$computerName all auto-start services are running." -ForegroundColor Green
            $results+=[pscustomobject]@{
                computername=$computerName
                nonRunningServices='none'
                failedToStart='none'
            }
        }
    }
    return $results
} 

startAllAutoServices $computerNames $skipServices

Prior version:

function startAllAutoServices($computer=$env:computername){
    # Start all non-running Auto-Start services    
    $nonRunningServices=Get-WmiObject win32_service -ComputerName $computer -Filter "startmode = 'auto' AND state != 'running' AND name != 'sppsvc'"
    $nonRunningServices|Invoke-WmiMethod -Name StartService

    # Detect non-running service
    $failedServices = Get-WmiObject win32_service -ComputerName $computer -Filter "startmode = 'auto' AND state != 'running' AND name != 'sppsvc'" | select -expand Name
    if($failedServices){
        write-host "$computer stopped services: $failedServices"
        return $false
    }else{
        write-host "$computer all auto-start services are running."
        return $true
    }    
}
startAllAutoServices

Leave a Reply

Your email address will not be published. Required fields are marked *