I was writing some powershell code to start a number of internally developed windows services in parallel. The services don't depend on other services and I ran the Start-Service command in a loop spawning a PSJob per Start-Service call.
However after reviewing the events generated by Service Control Manager it seems like only one service is in Starting state at anyone time.
Fully acknowledge I could have messed up the powershell code but I thought I would check to see there is not a fundamental Windows (Server 2003) limit in play here (ie only one service starting at a time).
Daniel
cls
$Services = $('Service1', 'Service2', 'Service3')
foreach($Service in $Services) {
Start-Job -ScriptBlock {
Param($ServiceName)
Start-Service $ServiceName
} -ArgumentList $Service
}
Get-Job | Wait-Job | Receive-Job<br/>
Had an anonymous contact look in the Windows Source code for me and the answer is that only 1 service may be starting at anyone time. This is enforced by the SCM taking a global lock on the services database before starting a service.
Related
I have a working PS script that gets the boot time of remote servers
#$ServerArray is the list of servers
$cso = New-CimSessionOption -Protocol Dcom
$cs = New-CimSession -ComputerName $ServerArray -SessionOption $cso
Get-CimInstance -CimSession $cs -ClassName win32_operatingsystem -OperationTimeoutSec 10 | select csname, Caption, lastbootuptime | sort lastbootuptime | format-table -autosize
Result:
csname Caption lastbootuptime
------ ------- --------------
server1 Microsoft Windows Server 2012 R2 Standard 10/30/2021 3:07:23 AM
server2 Microsoft Windows Server 2012 R2 Standard 10/30/2021 3:12:35 AM
server3 Microsoft Windows Server 2012 R2 Standard 10/30/2021 3:13:34 AM
Are there any other details that can be extracted using PowerShell or another API to show that the server has properly booted?
Note: This may be difficult to use PowerShell for depending on the mechanism of execution; PowerShell Remoting is not available during early stages of boot, nor is an interactive CLI. However, some agents or services (like VMware Tools as an example) can facilitate remote execution at times like these. Scheduled Tasks can also be leveraged to run code locally during some earlier boot phases. Though this answer centers around PowerShell, the general information can be used with other programming languages as well.
There are boot states of Windows you can attempt to look for ways to check these but the most reliable way I've found to know if the computer is in a state for a user to log on is to wait for Get-Process winlogon to return a process back. Once winlogon is running, the computer is near or able to facilitate logon sessions, pending completion of GPO application.
This SuperUser answer explains how to use the Windows Performance Toolkit (part of the Windows SDK) to initiate a boot trace and disseminate its report, but that isn't the focus of this question or answer. I'm including it here to show that waiting for winlogon is the right way to identify when the system is ready for interaction; execution of winlogon is actually the final Windows boot phase and that answer exemplifies this.
Note: Technically, logging in and waiting for scheduled tasks to complete on login is the final step but that portion comes after after kernel boot has completed, and can be repeated for multiple logon sessions. I don't consider this part of the "boot sequence" or system initialization.
As for the boot phases of Windows I've only been able to generate a report of a boot trace using xbootmgr. I'm not sure there is a documented API (win32 or otherwise) exposed to the userspace to check for the current boot phase outside of the boot trace. At this time I can only recommend looking at environmental details to know the current boot phase (such as checking for the winlogon process), although I'm not familiar enough with the environments of the other boot phases to make additional recommendations here.
If I learn more information about the other phases I will update this answer.
My goal is to remotely check a group of computers (extensive list) not only to see if the server has rebooted (usually when it was last rebooting), but if Windows is fully up and running at the login screen, and it won't restart for further updates or still be installing updates.
I did find a service called AppReadiness, which stopped it until the server rebooted. I am concerned that if it is set to manual, it may not always start. Could somebody please confirm if this is a reliable service?
EDIT: As I'm writing this, I did find out that it was stopped until it says "Working on updates, 100% complete, Don't turn off your computer" but as the server hung on that message, the AppReadiness service started. Is there anything better to watch? I've read other answers on different questions say to check if C$ is available, but that is available sooner than AppReadiness is available.
The code that is being used to check the service:
$creds = Get-Credential -Message "Enter server credentials:" -UserName "SERVERNAME\Administrator"
Get-WmiObject Win32_Service -ComputerName "SERVERIPADDRESS" -Credential $creds | Where-Object {$_.Name -eq "AppReadiness"}
EDIT 2: Also, instead of monitoring services, I have also tried looking for processes like winlogon.exe and loginui.exe for guidance on the server's condition but I'm not receiving the results I'm looking to record. These processes show when the server is getting ready when I was hoping they would only show once the login GUI was visible.
EDIT 3:
This edit is for the answer by #Kelv.Gonzales who stated to check for the Windows Event Log "DHCPv4 client service is started" log entry. That doesn't work and is on par with other services and events that I monitored. It shows valid before the login screen.
My code was:
$creds = Get-Credential -Message "Enter server credentials:" -UserName "SERVERNAME\Administrator"
$server = "IPADDRESSOFSERVER"
while($true)
{
$event = Get-WmiObject Win32_NTLogEvent -ComputerName $server -Credential $creds -Filter "(logfile='System' AND eventcode = '50036')" | select -First 1
$event.ConvertToDateTime($event.TimeWritten)
Start-Sleep -Seconds 5
}
That one liner will just fire off once of course. Any reason why you are using WMI, instead of the built-in PowerShell cmdlet - Get-Service?
My suggestion is use an WMI Event watcher, using what you already have in place but target the service and any dependent services and have that event notify you when the state is running.
Use PowerShell to Monitor and Respond to Events on Your Server
This article is using PowerShell and VBScript to do this, but you can do this with all PowerShell.
You can have a temporary or permanent watcher.
PowerShell and Events: WMI Temporary Event Subscriptions
Those can get a bit deep, so, if they aren't for you, you can just use your one line in a Do Loop that stops after the service comes online.
Basic example:
$TargetHost = $env:COMPUTERNAME
do {
$TargetOperation = Get-WmiObject Win32_Service -ComputerName $TargetHost |
Where-Object {$_.Name -eq "AppReadiness"}
"Checking host $TargetHost for service/process $($TargetOperation.Name)"
Start-Sleep -Seconds 3
} until (($TargetOperation).State -eq 'Running')
"Validation of host $TargetHost for service/process $($TargetOperation.Name) complete"
# Results
Checking host WS70 for service/process AppReadiness
Checking host WS70 for service/process AppReadiness
Checking host WS70 for service/process AppReadiness
Validation of host WS70 for service/process AppReadiness complete
You can of course add as many services or processes as you'd like using operation logic.
All the above applies to almost whatever you'd like to watch. Services, process, file-folder.
Or just use this script in the loop.
Get Remote Logon Status - Powershell
This script will return the logon status of the local or a remote
machine. Return types include "Not logged on", "Locked", "Logged
on", and "Offline.
The most useful part of this is to check whether a computer is in the
locked state, although the other return types could also be useful.
This is a simple function, and can easily be included in a larger
script. The return types could be changed to numbers for the calling
script to more easily parse the return value.
Download: GetRemoteLogonStatus.ps1
Would finding 0 updates queued accomplish what you need then? This arcticle goes into detail on how to accomplish it.
To check if windows is ready to log in, can you query for the event log for 'DHCPv4 client service is started' - Event ID 50036 satisfy if windows is ready.
I spent a week searching for a solution for this issue. I wanted to share what is currently my best solution, hopefully it helps someone.
#kelv-gonzales pointed me in the right direction with his comment about the Windows Modules Installer and TrustedInstaller.
Breakdown of my solution
There exists a process called TiWorker.exe that is the Windows Modules Installer Worker process. This is the process responsible for actually installing the Windows Update. This process (from what I've observed) reliably runs throughout the duration of a Windows Update being installed. Once the update has been installed and a pending reboot is required, this process stops and goes away.
When you reboot the machine to satisfy the pending reboot, the TiWorker.exe process is one of the very first processes to start while the machine is coming back up. In some cases, depending on the nature of the update, will finish the installation of an update (that's the increasing percentage you see during boot-up) while the computer comes back up. Once the login screen is available, this process typically remains running four roughly 2 minutes (from what I've observed). After which, it stops and goes away. I found that waiting these few minutes was a small price to pay for reliably knowing when updates have finished processing.
My scripts logic
I was able to write a script that keys off of the existence of this service. For example, below is a high-level flow of my script.
Initiate the installation of Windows Updates.
Wait for updates to finishing installing and for a pending reboot.
Once a pending reboot exists, wait until TiWorker.exe is no longer running and then trigger a reboot.
While the machine is rebooting, repeatedly check the status of the machine during the reboot. While the machine is coming back up, TiWorker.exe will start back up to finish the update. Keep waiting until TiWorker.exe is no longer running (typically, the update will finish and the OS will wait at the lock screen for about 2 minutes before this process stops).
Code
I modified the code posted by #gabriel-graves to check for the TiWorker.exe process.
$creds = Get-Credential -Message "Enter server credentials:" -UserName "SERVERNAME\Administrator"
Get-WmiObject Win32_Process -ComputerName "SERVERIPADDRESS" -Credential $creds | Where-Object {$_.Name -eq "TiWorker.exe"}
GOAL: Rename all AD Objects to new convention and move them to new OU's in a restructured AD hierarchy.
I have a PS script that utilizes the SCCM site module and the Active Directory module to do the following:
Get-CMCollectionMember
For each Member name, Remove-CMDevice
Move-ADObject of the same Member name
Rename-Computer of the same Member name
Reboot the computer
Run SCCM TriggerSchedules for DDR and Hardware Inventory
When I run the triggerschedules, the PSComputerName shows as the computer objects OLD name. Not the new one.
Everything else seems to work - the AD object is moved, then renamed. The 'new' SCCM Device Object shows up in SCCM (via Delta System Discovery - Interval 5 min).
The problem is that SCCM (the client, I suspect) is holding on to old object info.
My question is, short of just reinstalling the client...why is this happening? I am not renaming the object in SCCM - the first step is actually removing the devices from SCCM. They are then rediscovered via Delta system discovery.
When I run...
Invoke-WMIMethod -ComputerName WD001-WK100 -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule $schedule
...at the end of the process, PSComputername is the old name.
Any advice appreciated.
Reinstalling the SCCM Client is simple, and works. I am just running this on the end instead of running cycles, since they all run when the client is reinstalled, anyway. It also clears up my issue:
Install-CMClient -DeviceName $pc -AlwaysInstallClient $true -ForceReinstall $true
Thank you!
I'm trying to use Powershell to get the NextRunTime for some scheduled tasks. I'm retrieving the values but they don't match up to what I'm seeing in the Task Scheduler Management Console.
For example, in the Task Scheduler console my "TestTask" has a Next Run Time value of "1/9/2018 12:52:30 PM". But when I do the following call in Powershell it shows "12:52:52 PM" for the NextRunTime.
Get-ScheduledTask -TaskName "TestTask" | Get-ScheduledTaskInfo
From what I've seen the seconds value is always the same value as the minutes when returned from the PowerShell Get-ScheduledTaskInfo cmdlet. I'm wondering if there's a time formatting error (hh:mm:mm instead of hh:mm:ss) in that cmdlet but I have no idea how to look for that.
The task is running at the exact time shown in the console so that makes me think that it's an issue with the powershell call.
Has anyone seen this issue before and know how to get the correct NextRunTime value in PowerShell? I'm also seeing the same issue with the LastRunTime value.
I've tried this on Windows Server 2016 and Windows 10 and get the same results on both operating systems.
I can confirm that I see the same issue on Server 2012R2 as well. You can get the correct information by using the task scheduler COM object, getting the root folder (or whatever folder your task is stored in, but most likely its in the root folder), and then getting the task info from that. Here's how you'd do it:
$Scheduler = New-Object -ComObject Schedule.Service
$Scheduler.Connect()
$RootFolder = $Scheduler.GetFolder("\")
$Task = $RootFolder.GetTask("TestTask")
$Task.NextRunTime
Probably also worth noting that you can use the Connect() method to connect to the task scheduler on other computers (if you have rights to access their task scheduler), and get information about their tasks, stop or start their tasks, make new tasks... lots of good stuff here if you don't mind not using the *-ScheduledTask* cmdlets.
I am managing a lab for a technology conference, and I have a PowerShell script that does some cleanup between sessions, deleting leftover cruft from the desktop, resetting links in the Taskbar, etc. When I run this script "locally", meaning I use a shortcut while logged in as the generic lab user, it takes about 5 seconds to complete. However, when I run the same script using Remoting it takes nearly 10 minutes to complete. And this on a single machine. My worry is that I have 40 machines that need to refresh, and sometimes only 15 minutes total between sessions. The code I am testing with is pretty simple
Invoke-Command -computerName:$machine -argumentList: $file, $context –scriptblock {
param (
[String]$file,
[string]$context
)
& powershell.exe -noProfile -noLogo -executionPolicy bypass -file $file -context:$context
} -credential:$credential -authentication:CredSSP –asJob -jobName:$machine > $null
So, my question is, is this "normal", or is there some indication of a problem and I should really expect this to work at something closer to "local" speeds? FWIW, I am testing this on a VM.
Additionally, I am using a BallonTip to alert anyone who is sitting at the machine that a reset is happening. This works great when "manually" launching the script. However, when using Remoting the Ballon Tip never shows up. Of course neither does the console, so perhaps this is just a limitation of Remoting, that no UI can be triggered?
Any insights greatly appreciated. Thanks!
You won't be able to get a UI to show up like that since the script won't be running in the interactive user's session.
As for performance, I'm not entirely sure why would it take such a drastically longer time.
First, I would say try to remote into your local machine (instead of just running it locally), by passing either localhost, ., or the actual local computer name to the -ComputerName parameter of Invoke-Command. See if the performance is still as bad, or how it differs.
I'm also wondering if it has to do with spawning another powershell.exe process on the remote side. In theory it shouldn't, but what if you do this instead:
Invoke-Command -computerName:$machine -argumentList: $file, $context –scriptblock {
param (
[String]$file,
[string]$context
)
$sb = [ScriptBlock]::Create((Get-Content $file -Raw))
& $sb -context:$context
} -credential:$credential -authentication:CredSSP –asJob -jobName:$machine > $null