reboot
unknown
plain_text
a year ago
22 kB
5
Indexable
# Start Transcript for Logging Start-Transcript -Path "C:\Windows\Temp\DeloitteAutopilot_ManagedReboot_Staged.txt" -Append -IncludeInvocationHeader # Check presense of SCCM to ensure this doesn't run on already built machines. Exit success if so. $service = Get-Service ccmexec -ErrorAction SilentlyContinue if ($null -ne $service) { "SCCM Client is installed. Exiting script." Exit 0 } $ScriptBlock = { ####################################################### # Initial Checks - Make sure it's running at the right time ####################################################### # Start Transcript for Logging Start-Transcript -Path "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\DeloitteAutopilot_ManagedReboot.txt" -Append -IncludeInvocationHeader # Get the current username using WMI $username = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName # Check if the username is "*defaultuser0" + Check it's in ESP, if not Exit as well. $username = (Get-CimInstance -ClassName Win32_ComputerSystem -OperationTimeoutSec 120 | Select-Object -ExpandProperty Username); if ($username -like "*defaultuser0") { Write-Output "Device Provisioning - Exiting Script" Exit 0 } else { Write-Output "Account Provisioning - Beginning 'Managed Reboot' Logic v2" # Define registry path and value name $registryPath = "HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings" $valueName = "AccountSetupCategory.Status" $substringSucceeded = '"categoryState":"succeeded"' try { # Attempt to read the ESP status from the registry $strValue = (Get-ItemProperty -Path $registryPath -Name $valueName)."$valueName" # Determine ESP status based on registry value and presence of SecurityHealthSystray process $IsESPActive = if ($strValue.Contains($substringSucceeded)) { $false } else { # Check for SecurityHealthSystray process only if registry doesn't read "Succeeded" -not (Get-Process -Name "SecurityHealthSystray" -ErrorAction SilentlyContinue) } # If ESP is not active, exit with code 0 if (-not $IsESPActive) { Unregister-ScheduledTask -TaskName "Autopilot - Managed Reboot (will delete itself)" -Confirm:$false -ErrorAction SilentlyContinue Write-Output "Unregistered 'Autopilot - Managed Reboot (will delete itself)' because ESP is not active" Write-Output "ESP is not active, exiting code 0" exit 0 } } catch { # Assume ESP is active if there's an error reading the registry Write-Output "Error reading the registry. Assuming ESP is active." } } # Else Block Closing ####################################################### # Define our Functions ####################################################### function CheckSCCMInstallTask { $SCCMtaskName = "InstallSCCMClient" # Name of the MEMCM App produced task $checkInterval = 0.5 # Check interval in seconds (400 milliseconds) $maxTime = 30 # Maximum time to check in seconds (30 secs) $startTime = Get-Date $destinationFolder = "C:\MSICache\SCCMClient" while ($true) { $task2 = Get-ScheduledTask -TaskName "$SCCMtaskName" -ErrorAction SilentlyContinue if ($task2 -ne $null) { Write-Output "Scheduled task '$SCCMtaskName' exists. Proceed to reboot!" break } else { $currentTime = Get-Date $elapsedTime = ($currentTime - $startTime).TotalSeconds if ($elapsedTime -ge $maxTime) { Write-Output "Scheduled task '$SCCMtaskName' does not exist after 30 seconds. Let's check if SCCM content is present" if (Test-Path -Path "$destinationFolder\LaunchHidden.vbs") { Write-Output "Destination folder and script exist. Creating backup task..." $action = New-ScheduledTaskAction -Execute "Wscript.exe" -Argument "$destinationFolder\LaunchHidden.vbs /B /Nologo" $triggerAtLogon = New-ScheduledTaskTrigger -AtLogon $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -StartWhenAvailable Register-ScheduledTask -TaskName "InstallSCCMClientManagedReboot" -Action $action -Trigger $triggerAtLogon -Settings $settings -User "SYSTEM" -ErrorAction SilentlyContinue Write-Output "Backup InstallSCCMClientManagedReboot task has been created." } else { Write-Output "Destination folder or script does not exist. MEMCM Intune App Install likely failed." } break } } Start-Sleep -Seconds $checkInterval } } # CheckSCCMInstallTask Function Closing function SetSkipUserESPinRegistry { try { # Base path where the GUIDs and FirstSync are located $basePath = 'HKLM:\SOFTWARE\Microsoft\Enrollments' # Retrieve all subkeys under the base path and filter for 'FirstSync' $firstSyncKeys = Get-ChildItem -Path $basePath -Recurse | Where-Object { $_.PSChildName -eq 'FirstSync' } # Iterate over each FirstSync subkey foreach ($firstSyncKey in $firstSyncKeys) { # Define the full path to the FirstSync subkey $firstSyncPath = $firstSyncKey.PSPath # Check if the 'SkipUserStatusPage' property exists and update it $propertyExists = Get-ItemProperty -Path $firstSyncPath -Name 'SkipUserStatusPage' -ErrorAction SilentlyContinue if ($propertyExists) { # Update the 'SkipUserStatusPage' property Set-ItemProperty -Path $firstSyncPath -Name 'SkipUserStatusPage' -Value 4294967295 Write-Output "Property 'SkipUserStatusPage' updated successfully in $firstSyncPath." } } } catch { # Handle exceptions Write-Output "Error encountered: $_" } #Make sure Account Setup = Succeeded, if not update the overall success to Succeeded in case of issues with ESP. try { # Retrieve the current JSON data from the registry $currentValue = (Get-ItemProperty -Path $registryPath -Name $valueName).$valueName $jsonData = $currentValue | ConvertFrom-Json # Accessing specific subcategory correctly $appsSubcategoryState = $jsonData.'AccountSetup.AppsSubcategory'.subcategoryState # Check if the AppsSubcategory is already succeeded if ($appsSubcategoryState -eq 'succeeded') { Write-Output "App Category Registry has updated correctly to 'succeeded'. No manual intervention needed." } else { # Check the main category state and update if not already succeeded if ($jsonData.categoryState -ne 'succeeded') { $jsonData.categoryState = 'succeeded' $newJsonValue = $jsonData | ConvertTo-Json -Depth 100 -Compress Set-ItemProperty -Path $registryPath -Name $valueName -Value $newJsonValue Write-Output "Updated 'categoryState' to 'succeeded'. - ESP had issues reflecting Account Stage success" } else { Write-Output "Overall 'categoryState' is already set to 'succeeded' in registry. No manual intervention needed" } } } catch { Write-Output "Failed to check/update the registry. Error details: $_" } } # SetSkipUserESPinRegistry Function Closing function RebootSequence { # Unregister scheduled task named Managed Reboot try { Write-Output "Attempting to delete the scheduled task 'Autopilot - Managed Reboot (will delete itself)'..." Unregister-ScheduledTask -TaskName "Autopilot - Managed Reboot (will delete itself)" -Confirm:$false -ErrorAction Stop Write-Output "Scheduled task deleted successfully." } catch { Write-Output "Failed to delete scheduled task. Error: $_" } # Define the path for the new registry key to check $registryPath = "HKLM:\SOFTWARE\Deloitte" $keyName = "Managed Reboot" $registryKeyValue = 1 # Try to get the registry key property $keyExists = Get-ItemProperty -Path $registryPath -Name $keyName -ErrorAction SilentlyContinue if (-not $keyExists) { Write-Output "Managed Reboot hasn't run prior on this machine, okay to initiate reboot..." # Create or update the registry key try { New-ItemProperty -Path $registryPath -Name $keyName -Value $registryKeyValue -PropertyType DWORD -Force -ErrorAction Stop Write-Output "Created registry key: $keyName in $registryPath with value $registryKeyValue." } catch { Write-Output "Failed to create registry key: $_" } # In the event SCCM is the last app - ensure SCCM Install Task has been created before rebooting... CheckSCCMInstallTask # Record the script start time $scriptStartTime = Get-Date Write-Output "Reboot start time is $scriptStartTime" # First attempt to execute reboot action using PowerShell cmdlet Write-Output "Attempting initial reboot with shutdown -r" cmd /c shutdown -r -t 0 -f # Sleep to give the computer some time to initiate reboot Start-Sleep -Seconds 10 # Get the last boot up time $lastBootTime = (Get-CimInstance -ClassName win32_operatingsystem).LastBootUpTime Write-Output "Checking last boot time: $lastBootTime" # Check if the last boot time is greater than the script start time if ($lastBootTime -gt $scriptStartTime) { Write-Output "The machine has successfully rebooted. Exiting script." } else { Write-Output "Initial reboot attempt failed. Entering reboot retry loop..." # Retry loop using shutdown command do { Write-Output "Trying to reboot using shutdown -r..." cmd /c shutdown -r -t 0 -f # Sleep for a minute to allow shutdown command to execute Start-Sleep -Seconds 30 # Refresh the last boot time check $lastBootTime = (Get-CimInstance -ClassName win32_operatingsystem).LastBootUpTime Write-Output "Re-checking last boot time: $lastBootTime" # Check if the last boot time is now greater than the script start time if ($lastBootTime -gt $scriptStartTime) { Write-Output "The machine has now successfully rebooted. Exiting retry loop." break } else { Write-Output "Retry reboot attempt did not succeed. Will retry in 30 seconds..." Start-Sleep -Seconds 30 } } while ($true) } } else { Write-Output "Managed Reboot must have run just prior. Skipping reboot to prevent loop." } } # RebootSequence Function Closing ####################################################### # Main Script Function ####################################################### function CheckAllApps+Reboot { # Extract the domain and username (assumes format DOMAIN\username) $domain, $user = $username -split '\\', 2 try { # Check if either $domain or $user is empty and return early if ([string]::IsNullOrWhiteSpace($domain) -or [string]::IsNullOrWhiteSpace($user)) { Write-Output "User not found yet, Exiting.." Exit 0 } # Attempt to obtain the SID $objUser = New-Object System.Security.Principal.NTAccount($domain, $user) $userSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier]).Value if ([string]::IsNullOrWhiteSpace($userSID)) { Write-Output "No user SID found yet, Exiting.." Exit 0 } Write-Output "Found user SID: $userSID" } catch { # Handle any exception by just printing a simple message or even nothing at all Write-Output "An error occurred. Unable to proceed." Exit 0 } # Define the base registry path using the user SID $basePath = "HKLM:\SOFTWARE\Microsoft\Windows\Autopilot\EnrollmentStatusTracking\$userSID\Setup\Apps\Tracking\Sidecar" # Check if the registry path exists if (-not (Test-Path $basePath)) { Write-Output "App Sidecar Registry doesn't exist yet, Exiting.." Exit 0 } # Count all app GUIDs from registry $registryCount = (Get-ChildItem -Path $basePath).Count # Define the directory and pattern for log files $logDirectory = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs" $logPattern = "IntuneManagementExtension*.log" # Check if at least one log file exists initially if ((Get-ChildItem -Path $logDirectory -Filter $logPattern).Count -eq 0) { Write-Output "No log files found initially... Exiting" Exit 0 } # Fetch AppIDs from log files based on registry count $appKeys = @() $logFiles = Get-ChildItem -Path $logDirectory -Filter $logPattern foreach ($logFile in $logFiles) { $content = Get-Content -Path $logFile.FullName -Raw $pattern = '\[Win32App\]\[ESPAppLockInProcessor\] Found ' + $registryCount + ' apps which need to be installed for current phase of ESP. AppIds: ([a-f0-9\-,\s]+)\]' if ($content -match $pattern) { $appKeys += $matches[1] -split ', ' | ForEach-Object { New-Object PSObject -Property @{ Name = $_.Trim() } } break # Assuming only one relevant entry per log file } } if ($appKeys.Count -eq 0) { Write-Output "No App ID's found for the current User Stage of ESP in log files. Trying backup method..." # Backup method to obtain app IDs from the registry $appKeys = Get-ChildItem -Path $basePath | ForEach-Object { New-Object PSObject -Property @{ Name = $_.PSChildName } } if ($appKeys.Count -eq 0) { Write-Output "No App ID's found for the current User Stage of ESP using backup method. Exiting" Exit 0 } } # Main check loop for each app for ($index = 0; $index -lt $appKeys.Count; $index++) { $key = $appKeys[$index] $appID = $key.Name.Split('\')[-1] -replace '^Win32App_', '' -replace '_.*$' # Define two patterns to match in the log files $pattern1 = '\[Win32App\]\[ReportingManager\] Detection state for app with id: ' + [regex]::Escape($appID) + ' has been updated\. Report delta: {"DetectionState":{.*"NewValue":"Installed".*}}' $pattern2 = '\[Win32App\]\[ReportingManager\] Detection state for app with id: ' + [regex]::Escape($appID) + ' has been updated\. Report delta: {"EnforcementState":{"OldValue":"InProgress","NewValue":"Error".*}}' $pattern3 = '\[Win32App\]\[DetectionActionHandler\] Detection for policy with id: ' + [regex]::Escape($appID) + ' resulted in action status: Success and detection state: Detected.' $appInstalled = $false do { # Refresh the log files list $logFiles = Get-ChildItem -Path $logDirectory -Filter $logPattern foreach ($logFile in $logFiles) { # Load the log file content $logFileContent = Get-Content -Path $logFile.FullName -Raw if ($logFileContent -match $pattern1 -or $logFileContent -match $pattern2 -or $logFileContent -match $pattern3) { Write-Output "Detected App with ID: $appID as Installed (or at least was downloaded & errored)" $appInstalled = $true break } } if (-not $appInstalled) { # Adjust the sleep time if it's the last app in the list if ($index -eq $appKeys.Count - 1) { if (-not $lastMessageShown) { Write-Output "Waiting on the Last app $appID... I won't spam this one." $lastMessageShown = $true # Ensure the message is shown only once } Start-Sleep -Seconds 2 # Reduced sleep interval for the final app } else { Write-Output "Waiting for App ID $appID..." Start-Sleep -Seconds 30 # Regular interval for other apps } } } while (-not $appInstalled) } Write-Output "Wooohooo - All Account Stage Apps are Detected!!!!" # Let's begin the logic for rebooting / forcing registry keys. try { $succeeded = 'succeeded' $identifying = 'Apps (Identifying)' # Update this as needed for your specific use-case $maxAttempts = 60 $attemptCount = 0 while ($attemptCount -lt $maxAttempts) { $currentValue = (Get-ItemProperty -Path $registryPath -Name $valueName).$valueName $jsonData = $currentValue | ConvertFrom-Json $appsSubcategoryState = $jsonData.'AccountSetup.AppsSubcategory'.subcategoryState $appsSubcategoryStatusText = $jsonData.'AccountSetup.AppsSubcategory'.subcategoryStatusText # Check if state has succeeded if ($appsSubcategoryState -eq $succeeded) { Write-Output "App Category Registry has updated correctly to '$succeeded'. No manual intervention needed." RebootSequence return # Exit the script after successful reboot sequence } # Adjust sleep time based on attempt count if ($attemptCount -lt 20) { Start-Sleep -Milliseconds 400 # Rapid check phase } else { Write-Output "Apps are either in 'Identifying' stage or 'X out of X Apps'. Checking again in 5 seconds." Start-Sleep -Seconds 5 # Slower check phase } $attemptCount++ } # Actions to perform if max attempts are reached without successful state if ($appsSubcategoryState -ne $succeeded) { Write-Output "Took too long for ESP GUI to reflect apps installed. Let's try via skipUserESP registry method and initiate a reboot immediately." # Stop the Intune service to ensure the changes persist Stop-Service -Name "IntuneManagementExtension" -Force -ErrorAction SilentlyContinue # Force registry to skip User ESP on reboot SetSkipUserESPinRegistry # Call function to initiate reboot RebootSequence } } catch { Write-Output "Error encountered: $_" } } # CheckAllApp+Reboot Function Closing # Call the function CheckAllApps+Reboot # Stop logging (if applicable) Stop-Transcript } # Script Block Closing Bracket ####################################################### # Scheduled Task Creation - Code that runs at the time of platform script deployment ####################################################### # Define the path for the PowerShell script in the temporary directory $TempDir = [System.IO.Path]::GetTempPath() $ScriptFileName = "Autopilot_Managed_Reboot.ps1" $ScriptPath = Join-Path -Path $TempDir -ChildPath $ScriptFileName # Ensure the directory exists if (-not (Test-Path -Path $TempDir)) { New-Item -ItemType Directory -Path $TempDir -Force } # Convert the script block to a string and save it to the temporary file, overwriting any existing file $ScriptBlock.ToString() | Out-File -FilePath $ScriptPath -Encoding UTF8 -Force # Create a new scheduled task action to execute the PowerShell script from the file $Action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$ScriptPath`"" # Define triggers for the scheduled task $IntervalTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Hours 24) $StartupTrigger = New-ScheduledTaskTrigger -AtStartup $LogonTrigger = New-ScheduledTaskTrigger -AtLogon # Combine all triggers $Triggers = @($IntervalTrigger, $StartupTrigger, $LogonTrigger) # Define settings for the scheduled task $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -StartWhenAvailable # Register the new scheduled task to run as SYSTEM $TaskName = "Autopilot - Managed Reboot (will delete itself)" $TaskDescription = "Handles reboot of Last ESP App and then deletes itself. Also executes at system startup/user login." Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Triggers -Settings $Settings -Description $TaskDescription -User "SYSTEM" -Force # Output a confirmation message Write-Output "Scheduled task '$TaskName' created successfully. It will execute as SYSTEM, using the script from '$ScriptPath', checking every 5 minutes and at every system startup & login." # Start the scheduled task Start-ScheduledTask -TaskName $TaskName # Confirmation of task start Write-Output "Scheduled task '$TaskName' started successfully." # Stop logging (if applicable) Stop-Transcript
Editor is loading...
Leave a Comment