diff --git a/data/os/Windows.yaml b/data/os/Windows.yaml
index 240339c60..e94b030bc 100644
--- a/data/os/Windows.yaml
+++ b/data/os/Windows.yaml
@@ -55,7 +55,9 @@ windows:
relops_az: "https://roninpuppetassets.blob.core.windows.net/binaries/taskcluster"
relops_s3: "https://s3-us-west-2.amazonaws.com/ronin-puppet-package-repo/Windows/taskcluster"
download_url: "https://github.com/taskcluster/taskcluster/releases/download"
- version: "93.1.1"
+ version: "93.1.4"
+ hw_version: "91.1.0"
+
generic-worker:
name:
amd64: "generic-worker-multiuser-windows-amd64"
diff --git a/data/roles/win116424h2hw.yaml b/data/roles/win116424h2hw.yaml
index 7e7ef0f83..efe65b488 100644
--- a/data/roles/win116424h2hw.yaml
+++ b/data/roles/win116424h2hw.yaml
@@ -18,5 +18,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/data/roles/win116424h2hwalpha.yaml b/data/roles/win116424h2hwalpha.yaml
index 62e6718af..717a61798 100644
--- a/data/roles/win116424h2hwalpha.yaml
+++ b/data/roles/win116424h2hwalpha.yaml
@@ -18,5 +18,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/data/roles/win116424h2hwperfsheriff.yaml b/data/roles/win116424h2hwperfsheriff.yaml
index 2d4d94c34..eeffd0093 100644
--- a/data/roles/win116424h2hwperfsheriff.yaml
+++ b/data/roles/win116424h2hwperfsheriff.yaml
@@ -19,5 +19,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/data/roles/win116424h2hwref.yaml b/data/roles/win116424h2hwref.yaml
index 917cf305e..7393de1b7 100644
--- a/data/roles/win116424h2hwref.yaml
+++ b/data/roles/win116424h2hwref.yaml
@@ -18,5 +18,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/data/roles/win116424h2hwrefalpha.yaml b/data/roles/win116424h2hwrefalpha.yaml
index 7551367e6..5c0c99905 100644
--- a/data/roles/win116424h2hwrefalpha.yaml
+++ b/data/roles/win116424h2hwrefalpha.yaml
@@ -18,5 +18,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/data/roles/win116424h2hwrelops1213.yaml b/data/roles/win116424h2hwrelops1213.yaml
index a0ca52cf8..4ea5ee74c 100644
--- a/data/roles/win116424h2hwrelops1213.yaml
+++ b/data/roles/win116424h2hwrelops1213.yaml
@@ -19,5 +19,3 @@ win-worker:
## Use to overwrite values in the Windows yaml file.
## Replace windows. with win-worker.variant.
variant:
- taskcluster:
- version: "91.1.0"
diff --git a/modules/roles_profiles/manifests/profiles/disable_services.pp b/modules/roles_profiles/manifests/profiles/disable_services.pp
index 5d4ce9175..1617def02 100644
--- a/modules/roles_profiles/manifests/profiles/disable_services.pp
+++ b/modules/roles_profiles/manifests/profiles/disable_services.pp
@@ -39,17 +39,36 @@
include win_disable_services::disable_windows_update
if $facts['custom_win_purpose'] != builder {
include win_disable_services::disable_wsearch
- ## Let's Uninstall Appx Packages
- ## Taken from https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool
- ## Bug 1913499 https://bugzilla.mozilla.org/show_bug.cgi?id=1913499
- include win_disable_services::uninstall_appx_packages
- if ($facts['custom_win_location'] == 'azure') {
- include win_scheduled_tasks::kill_local_clipboard
- }
+ ## WIP for RELOPS-1946
+ ## Not currently working. Leaving n place for ref.
+ #include win_disable_services::disable_defender_smartscreen
+ #include win_disable_services::disable_sync_from_cloud
if $facts['custom_win_release_id'] == '2004' or '2009' {
## win11 ref with osdcloud
include win_disable_services::disable_windows_defender_schtask
}
+ case $facts['custom_win_location'] {
+ 'datacenter': {
+ $apx_uninstall = 'hw-uninstall.ps1'
+ include win_disable_services::disable_optional_services
+ }
+ 'azure': {
+ include win_scheduled_tasks::kill_local_clipboard
+ $apx_uninstall = 'uninstall.ps1'
+ }
+ default: {
+ }
+ }
+ ## Let's Uninstall Appx Packages
+ ## Taken from https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool
+ ## Bug 1913499 https://bugzilla.mozilla.org/show_bug.cgi?id=1913499
+ class { 'win_disable_services::uninstall_appx_packages':
+ apx_uninstall => $apx_uninstall
+ }
+ ## must be ran after apx uninstall
+ if ($facts['custom_win_location'] == 'datacenter') {
+ include win_disable_services::disable_ms_edge
+ }
}
# May be needed for non-hardaware
# Commented out because this will break the auto restore
diff --git a/modules/roles_profiles/manifests/profiles/scheduled_tasks.pp b/modules/roles_profiles/manifests/profiles/scheduled_tasks.pp
index 7fcb53c8e..a4db1b25a 100644
--- a/modules/roles_profiles/manifests/profiles/scheduled_tasks.pp
+++ b/modules/roles_profiles/manifests/profiles/scheduled_tasks.pp
@@ -11,8 +11,9 @@
$startup_script = 'azure-maintainsystem.ps1'
}
'datacenter': {
- #$startup_script = 'maintainsystem-hw.ps1'
- $startup_script = 'maintainsystem-reftester.ps1'
+ $startup_script = 'maintainsystem-hw.ps1'
+ include win_scheduled_tasks::self_redeploy_check
+ include win_scheduled_tasks::gw_exe_check
}
default: {
$startup_script = 'maintainsystem.ps1'
diff --git a/modules/roles_profiles/manifests/profiles/windows_generic_worker_standalone.pp b/modules/roles_profiles/manifests/profiles/windows_generic_worker_standalone.pp
index 0b58360a8..5c170780c 100644
--- a/modules/roles_profiles/manifests/profiles/windows_generic_worker_standalone.pp
+++ b/modules/roles_profiles/manifests/profiles/windows_generic_worker_standalone.pp
@@ -9,7 +9,7 @@
$arch = 'win64'
- $ext_pkg_src_loc = lookup('windows.taskcluster.relops_az')
+ $ext_pkg_src_loc = "${lookup('windows.taskcluster.relops_az')}/"
$generic_worker_dir = lookup('windows.dir.generic_worker')
$gw_exe_path = "${generic_worker_dir}\\generic-worker.exe"
@@ -51,14 +51,14 @@
generic_worker_dir => $generic_worker_dir,
gw_config_path => $gw_config_path,
gw_exe_path => $gw_exe_path,
- gw_exe_source => "${ext_pkg_src_loc}/${taskcluster_version}/${gw_name}",
+ gw_exe_source => "${ext_pkg_src_loc}${taskcluster_version}/${gw_name}",
gw_status => $facts['custom_win_genericworker_service'],
livelog_exe => "${facts['custom_win_systemdrive']}\\\\generic-worker\\\\livelog.exe",
- livelog_exe_source => "${ext_pkg_src_loc}/${$taskcluster_version}/${livelog_name}",
+ livelog_exe_source => "${ext_pkg_src_loc}${taskcluster_version}/${livelog_name}",
task_dir => "${facts['custom_win_systemdrive']}\\\\",
taskcluster_access_token => lookup('taskcluster_access_token'),
taskcluster_proxy_exe => "${facts['custom_win_systemdrive']}\\\\generic-worker\\\\taskcluster-proxy.exe",
- taskcluster_proxy_source => "${ext_pkg_src_loc}/${taskcluster_version}/${proxy_name}",
+ taskcluster_proxy_source => "${ext_pkg_src_loc}${taskcluster_version}/${proxy_name}",
taskcluster_root => lookup('windows.taskcluster.root_url'),
#task_user_init_cmd => $init,
worker_type => $worker_pool_id,
diff --git a/modules/roles_profiles/manifests/profiles/windows_worker_runner.pp b/modules/roles_profiles/manifests/profiles/windows_worker_runner.pp
index 08ffb3f94..2284a628b 100644
--- a/modules/roles_profiles/manifests/profiles/windows_worker_runner.pp
+++ b/modules/roles_profiles/manifests/profiles/windows_worker_runner.pp
@@ -11,20 +11,18 @@
case $facts['custom_win_location'] {
'datacenter': {
- $ext_pkg_src_loc = lookup('windows.taskcluster.relops_s3')
+ $ext_pkg_src_loc = "${lookup('windows.taskcluster.relops_az')}/"
$provider = 'standalone'
+ $taskcluster_version =
+ lookup(['win-worker.variant.taskcluster.version', 'windows.taskcluster.hw_version'])
}
default: {
- #$ext_pkg_src_loc = lookup('windows.taskcluster.relops_az')
$ext_pkg_src_loc = "${lookup('windows.taskcluster.download_url')}/v"
- #$ext_pkg_src_loc = "https://github.com/taskcluster/taskcluster/releases/download/v93.1.4/generic-worker-multiuser-windows-arm64"
$provider = lookup('windows.taskcluster.worker_runner.provider')
+ $taskcluster_version =
+ lookup(['win-worker.variant.taskcluster.version', 'windows.taskcluster.version'])
}
}
-
- $taskcluster_version =
- lookup(['win-worker.variant.taskcluster.version', 'windows.taskcluster.version'])
-
case $facts['custom_win_os_arch'] {
'aarch64': {
$gw_name = lookup('windows.taskcluster.generic-worker.name.arm64')
diff --git a/modules/roles_profiles/manifests/roles/win116424h2hwrelops1213.pp b/modules/roles_profiles/manifests/roles/win116424h2hwrelops1213.pp
index c4c34b649..9e876d4a8 100644
--- a/modules/roles_profiles/manifests/roles/win116424h2hwrelops1213.pp
+++ b/modules/roles_profiles/manifests/roles/win116424h2hwrelops1213.pp
@@ -21,6 +21,7 @@
# Adminstration
include roles_profiles::profiles::logging
+ include roles_profiles::profiles::mercurial
include roles_profiles::profiles::nuc_management
#include roles_profiles::profiles::vnc
@@ -28,7 +29,8 @@
include roles_profiles::profiles::git
include roles_profiles::profiles::mozilla_build
include roles_profiles::profiles::mozilla_maintenance_service
- include roles_profiles::profiles::windows_generic_worker_standalone
include roles_profiles::profiles::windows_datacenter_administrator
include roles_profiles::profiles::google_chrome
+ #include roles_profiles::profiles::windows_generic_worker_standalone
+ include roles_profiles::profiles::windows_worker_runner
}
diff --git a/modules/win_disable_services/files/appxpackages/hw-uninstall.ps1 b/modules/win_disable_services/files/appxpackages/hw-uninstall.ps1
new file mode 100644
index 000000000..42bd7dcd1
--- /dev/null
+++ b/modules/win_disable_services/files/appxpackages/hw-uninstall.ps1
@@ -0,0 +1,334 @@
+function Write-Log {
+ param (
+ [string] $message,
+ [ValidateSet('DEBUG','INFO','WARN','ERROR')]
+ [string] $severity = 'INFO',
+ [string] $source = 'BootStrap',
+ [string] $logName = 'Application'
+ )
+
+ $entryType = 'Information'
+ $eventId = 1
+
+ switch ($severity) {
+ 'DEBUG' { $entryType = 'SuccessAudit'; $eventId = 2; break }
+ 'WARN' { $entryType = 'Warning'; $eventId = 3; break }
+ 'ERROR' { $entryType = 'Error'; $eventId = 4; break }
+ default { $entryType = 'Information'; $eventId = 1; break }
+ }
+
+ # Best-effort event log creation (avoid terminating failures / races)
+ try {
+ if (!([Diagnostics.EventLog]::Exists($logName)) -or
+ !([Diagnostics.EventLog]::SourceExists($source))) {
+ New-EventLog -LogName $logName -Source $source -ErrorAction SilentlyContinue | Out-Null
+ }
+ } catch {
+ # ignore
+ }
+
+ try {
+ Write-EventLog -LogName $logName -Source $source `
+ -EntryType $entryType -Category 0 -EventID $eventId `
+ -Message $message -ErrorAction SilentlyContinue
+ } catch {
+ # ignore
+ }
+
+ if ([Environment]::UserInteractive) {
+ $fc = @{
+ 'Information' = 'White'
+ 'Error' = 'Red'
+ 'Warning' = 'DarkYellow'
+ 'SuccessAudit' = 'DarkGray'
+ }[$entryType]
+ Write-Host $message -ForegroundColor $fc
+ }
+}
+
+# IMPORTANT: use 'Continue' so normal AppX noise doesn't hard-fail Puppet
+$ErrorActionPreference = 'Continue'
+
+$svcName = 'AppXSvc'
+$svcKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\AppXSvc'
+
+function Remove-PreinstalledAppxPackages {
+ [CmdletBinding()]
+ param()
+
+ $apps = @{
+ "Bing Search" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9nzbf4gt040c"; Description="Web Search from Microsoft Bing provides web results and answers in Windows Search" }
+ "Clipchamp.Clipchamp" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9p1j8s7ccwwt?hl=en-us&gl=US"; Description="Create videos with a few clicks" }
+ "Microsoft.549981C3F5F10" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/cortana/9NFFX4SZZ23L?hl=en-us&gl=US"; Description="Cortana (could not update)" }
+ "Microsoft.BingNews" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-news/9wzdncrfhvfw"; Description="Microsoft News app" }
+ "Microsoft.BingWeather" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/msn-weather/9wzdncrfj3q2"; Description="MSN Weather app" }
+ ## Doesn't actually gets removed
+ ## Comment out for now
+ #"Microsoft.DesktopAppInstaller" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9NBLGGH4NNS1"; Description="Microsoft App Installer for Windows 10 makes sideloading Windows apps easy" }
+ "Microsoft.GetHelp" = @{ VDIState="Unchanged"; URL="https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/customize-get-help-app"; Description="App that facilitates free support for Microsoft products" }
+ "Microsoft.Getstarted" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-tips/9wzdncrdtbjj"; Description="Windows 10 tips app" }
+ "Microsoft.MicrosoftOfficeHub" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/office/9wzdncrd29v9"; Description="Office UWP app suite" }
+ "Microsoft.Office.OneNote" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/onenote-for-windows-10/9wzdncrfhvjl"; Description="Office UWP OneNote app" }
+ "Microsoft.MicrosoftSolitaireCollection" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-solitaire-collection/9wzdncrfhwd2"; Description="Solitaire suite of games" }
+ "Microsoft.MicrosoftStickyNotes" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-sticky-notes/9nblggh4qghw"; Description="Note-taking app" }
+ "Microsoft.OutlookForWindows" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9NRX63209R7B?hl=en-us&gl=US"; Description="New Outlook app" }
+ "Microsoft.MSPaint" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/store/detail/paint-3d/9NBLGGH5FV99"; Description="Paint 3D app" }
+ "Microsoft.Paint" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9PCFS5B6T72H"; Description="Classic Paint app" }
+ "Microsoft.People" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-people/9nblggh10pg8"; Description="Contact management app" }
+ "Microsoft.PowerAutomateDesktop" = @{ VDIState="Unchanged"; URL="https://flow.microsoft.com/en-us/desktop/"; Description="Power Automate Desktop" }
+ "Microsoft.ScreenSketch" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/snip-sketch/9mz95kl8mr0l"; Description="Snip and Sketch app" }
+ "Microsoft.SkypeApp" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/skype/9wzdncrfj364"; Description="Skype app" }
+ "Microsoft.StorePurchaseApp" = @{ VDIState="Unchanged"; URL=""; Description="Store purchase app helper" }
+ "Microsoft.Todos" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-to-do-lists-tasks-reminders/9nblggh5r558"; Description="Microsoft To Do" }
+ "Microsoft.WinDbg.Fast" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9PGJGD53TN86?hl=en-us&gl=US"; Description="WinDbg" }
+ "Microsoft.Windows.DevHome" = @{ VDIState="Unchanged"; URL="https://learn.microsoft.com/en-us/windows/dev-home/"; Description="Dev Home dashboard" }
+ "Microsoft.Windows.Photos" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/microsoft-photos/9wzdncrfjbh4"; Description="Photos app" }
+ "Microsoft.WindowsAlarms" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-alarms-clock/9wzdncrfj3pr"; Description="Alarms & Clock" }
+ "Microsoft.WindowsCalculator" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-calculator/9wzdncrfhvn5"; Description="Calculator" }
+ "Microsoft.WindowsCamera" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-camera/9wzdncrfjbbg"; Description="Camera" }
+ "microsoft.windowscommunicationsapps" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/mail-and-calendar/9wzdncrfhvqm"; Description="Mail & Calendar" }
+ "Microsoft.WindowsFeedbackHub" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/feedback-hub/9nblggh4r32n"; Description="Feedback Hub" }
+ "Microsoft.WindowsMaps" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-maps/9wzdncrdtbvb"; Description="Maps" }
+ "Microsoft.WindowsNotepad" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-notepad/9msmlrh6lzf3"; Description="Notepad (Store)" }
+ "Microsoft.WindowsStore" = @{ VDIState="Unchanged"; URL="https://blogs.windows.com/windowsexperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/"; Description="Microsoft Store" }
+ "Microsoft.WindowsSoundRecorder" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-voice-recorder/9wzdncrfhwkn"; Description="Voice Recorder" }
+ "Microsoft.WindowsTerminal" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701"; Description="Windows Terminal" }
+ "Microsoft.Winget.Platform.Source"= @{ VDIState="Unchanged"; URL="https://learn.microsoft.com/en-us/windows/package-manager/winget/"; Description="Winget source" }
+ "Microsoft.Xbox.TCUI" = @{ VDIState="Unchanged"; URL="https://docs.microsoft.com/en-us/gaming/xbox-live/features/general/tcui/live-tcui-overview"; Description="Xbox TCUI" }
+ "Microsoft.XboxIdentityProvider" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/xbox-identity-provider/9wzdncrd1hkw"; Description="Xbox Identity Provider" }
+ "Microsoft.XboxSpeechToTextOverlay" = @{ VDIState="Unchanged"; URL="https://support.xbox.com/help/account-profile/accessibility/use-game-chat-transcription"; Description="Xbox chat transcription" }
+ "Microsoft.YourPhone" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/Your-phone/9nmpj99vjbwv"; Description="Phone Link" }
+ "Microsoft.ZuneMusic" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/groove-music/9wzdncrfj3pt"; Description="Groove Music" }
+ "Microsoft.ZuneVideo" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/movies-tv/9wzdncrfj3p2"; Description="Movies & TV" }
+ "MicrosoftCorporationII.QuickAssist" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/9P7BP5VNWKX5?hl=en-us&gl=US"; Description="Quick Assist" }
+ "MicrosoftWindows.Client.WebExperience" = @{ VDIState="Unchanged"; URL=""; Description="Windows 11 Web Experience" }
+ "Microsoft.XboxApp" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/store/apps/9wzdncrfjbd8"; Description="Xbox Console Companion" }
+ "Microsoft.MixedReality.Portal" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/mixed-reality-portal/9ng1h8b3zc7m"; Description="Mixed Reality Portal" }
+ "Microsoft.Microsoft3DViewer" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/p/3d-viewer/9nblggh42ths"; Description="3D Viewer" }
+ "MicrosoftTeams" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/xp8bt8dw290mpq"; Description="Microsoft Teams" }
+ "MSTeams" = @{ VDIState="Unchanged"; URL="https://apps.microsoft.com/detail/xp8bt8dw290mpq"; Description="Microsoft Teams (alt id)" }
+ "Microsoft.OneDriveSync" = @{ VDIState="Unchanged"; URL="https://docs.microsoft.com/en-us/onedrive/one-drive-sync"; Description="OneDrive sync app" }
+ "Microsoft.Wallet" = @{ VDIState="Unchanged"; URL="https://www.microsoft.com/en-us/payments"; Description="Microsoft Pay" }
+ }
+
+ foreach ($Key in $apps.Keys) {
+ try {
+ Write-Log -message ("uninstall_appx_packages :: removing AppX match: {0}" -f $Key) -severity 'DEBUG'
+
+ # Provisioned packages (image-level)
+ try {
+ Get-AppxProvisionedPackage -Online -ErrorAction Stop |
+ Where-Object { $_.PackageName -like ("*{0}*" -f $Key) } |
+ ForEach-Object {
+ $pkgName = $_.PackageName
+ try {
+ Remove-AppxProvisionedPackage -Online -PackageName $pkgName -ErrorAction Stop | Out-Null
+ } catch {
+ Write-Log -message ("Remove-AppxProvisionedPackage failed for {0}: {1}" -f $pkgName, $_.Exception.Message) -severity 'WARN'
+ }
+ }
+ } catch {
+ Write-Log -message ("Get/Remove provisioned package failed for key {0}: {1}" -f $Key, $_.Exception.Message) -severity 'WARN'
+ }
+
+ # Installed packages (all users)
+ try {
+ Get-AppxPackage -AllUsers -Name ("*{0}*" -f $Key) -ErrorAction SilentlyContinue |
+ ForEach-Object {
+ $full = $_.PackageFullName
+ try {
+ Remove-AppxPackage -AllUsers -Package $full -ErrorAction Stop | Out-Null
+ } catch {
+ Write-Log -message ("Remove-AppxPackage(-AllUsers) failed for {0}: {1}" -f $full, $_.Exception.Message) -severity 'WARN'
+ }
+ }
+ } catch {
+ Write-Log -message ("Get/Remove AppxPackage(-AllUsers) failed for key {0}: {1}" -f $Key, $_.Exception.Message) -severity 'WARN'
+ }
+
+ # Installed packages (current user)
+ try {
+ Get-AppxPackage -Name ("*{0}*" -f $Key) -ErrorAction SilentlyContinue |
+ ForEach-Object {
+ $full = $_.PackageFullName
+ try {
+ Remove-AppxPackage -Package $full -ErrorAction Stop | Out-Null
+ } catch {
+ Write-Log -message ("Remove-AppxPackage failed for {0}: {1}" -f $full, $_.Exception.Message) -severity 'WARN'
+ }
+ }
+ } catch {
+ Write-Log -message ("Get/Remove AppxPackage failed for key {0}: {1}" -f $Key, $_.Exception.Message) -severity 'WARN'
+ }
+ } catch {
+ # Absolutely never let AppX errors terminate this script (Puppet signal should be AppXSvc-only)
+ Write-Log -message ("Remove-PreinstalledAppxPackages unexpected failure for key {0}: {1}" -f $Key, $_.Exception.ToString()) -severity 'WARN'
+ continue
+ }
+ }
+}
+
+function Disable-AppXSvcCore {
+ [CmdletBinding()]
+ param()
+
+ $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
+ if ($null -ne $svc) {
+ if ($svc.Status -ne 'Stopped') {
+ Stop-Service -Name $svcName -Force -ErrorAction SilentlyContinue
+ }
+ Set-Service -Name $svcName -StartupType Disabled -ErrorAction SilentlyContinue
+ }
+
+ # Extra-hard disable (best-effort): do NOT allow sc.exe exit code to poison overall script exit code
+ try {
+ & sc.exe config $svcName start= disabled | Out-Null
+ } catch {
+ # ignore
+ } finally {
+ $global:LASTEXITCODE = 0
+ }
+
+ # Registry is the source of truth for disabled start
+ if (Test-Path $svcKeyPath) {
+ New-ItemProperty -Path $svcKeyPath -Name Start -Value 4 -PropertyType DWord -Force | Out-Null
+ }
+}
+
+function Ensure-AppXSvcHardeningTask {
+ [CmdletBinding()]
+ param()
+
+ $hardeningDir = 'C:\ProgramData\AppXLock'
+ $hardeningFile = Join-Path $hardeningDir 'Disable-AppXSvc.ps1'
+
+ if (-not (Test-Path $hardeningDir)) {
+ New-Item -ItemType Directory -Path $hardeningDir -Force | Out-Null
+ }
+
+ $hardeningScript = @'
+param()
+
+$ErrorActionPreference = "SilentlyContinue"
+
+$svcName = "AppXSvc"
+$svcKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Services\AppXSvc"
+
+try {
+ $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
+ if ($null -ne $svc) {
+ if ($svc.Status -ne "Stopped") {
+ Stop-Service -Name $svcName -Force -ErrorAction SilentlyContinue
+ }
+ Set-Service -Name $svcName -StartupType Disabled -ErrorAction SilentlyContinue
+ }
+
+ # Best-effort: do NOT leak sc.exe exit code
+ try {
+ & sc.exe config $svcName start= disabled | Out-Null
+ } catch {
+ # ignore
+ } finally {
+ $global:LASTEXITCODE = 0
+ }
+
+ if (Test-Path $svcKeyPath) {
+ New-ItemProperty -Path $svcKeyPath -Name Start -Value 4 -PropertyType DWord -Force | Out-Null
+ }
+} catch {
+ # best-effort only
+}
+'@
+
+ Set-Content -Path $hardeningFile -Value $hardeningScript -Encoding UTF8 -Force
+
+ $action = New-ScheduledTaskAction -Execute 'powershell.exe' `
+ -Argument "-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$hardeningFile`""
+ $trigger = New-ScheduledTaskTrigger -AtStartup
+
+ $taskName = 'Hard-Disable-AppXSvc'
+ $taskPath = '\Hardening\'
+
+ Unregister-ScheduledTask -TaskName $taskName -TaskPath $taskPath -Confirm:$false -ErrorAction SilentlyContinue
+
+ Register-ScheduledTask -TaskName $taskName `
+ -TaskPath $taskPath `
+ -Action $action `
+ -Trigger $trigger `
+ -RunLevel Highest `
+ -User 'SYSTEM' `
+ -Force | Out-Null
+}
+
+function Test-AppXSvcDisabled {
+ [CmdletBinding()]
+ param()
+
+ $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
+ if ($null -eq $svc) { return $true }
+
+ # Registry is the most reliable indicator (Start=4)
+ $regStart = $null
+ try {
+ $regStart = (Get-ItemProperty -Path $svcKeyPath -Name Start -ErrorAction SilentlyContinue).Start
+ } catch { }
+
+ $regDisabled = ($regStart -eq 4)
+
+ # CIM is a helpful second signal (StartMode: Auto/Manual/Disabled)
+ $cimDisabled = $false
+ $cimStartMode = 'Unknown'
+ try {
+ $svcCim = Get-CimInstance Win32_Service -Filter "Name='$svcName'" -ErrorAction SilentlyContinue
+ if ($svcCim) {
+ $cimStartMode = $svcCim.StartMode
+ $cimDisabled = ($svcCim.StartMode -eq 'Disabled')
+ }
+ } catch { }
+
+ if ($svc.Status -eq 'Stopped' -and ($regDisabled -or $cimDisabled)) {
+ return $true
+ }
+
+ return $false
+}
+
+# --- Main flow ---------------------------------------------------------------
+
+try {
+ Write-Log -message 'uninstall_appx_packages :: begin' -severity 'DEBUG'
+
+ Write-Log -message 'uninstall_appx_packages :: Remove-PreinstalledAppxPackages' -severity 'DEBUG'
+ Remove-PreinstalledAppxPackages
+
+ Write-Log -message 'uninstall_appx_packages :: Disable-AppXSvcCore' -severity 'DEBUG'
+ Disable-AppXSvcCore
+
+ Write-Log -message 'uninstall_appx_packages :: Ensure-AppXSvcHardeningTask' -severity 'DEBUG'
+ Ensure-AppXSvcHardeningTask
+
+ if (-not (Test-AppXSvcDisabled)) {
+ $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
+ $status = if ($svc) { $svc.Status } else { 'Missing' }
+
+ $regStart = $null
+ try { $regStart = (Get-ItemProperty -Path $svcKeyPath -Name Start -ErrorAction SilentlyContinue).Start } catch { }
+ $regStartStr = if ($null -ne $regStart) { $regStart } else { 'Missing' }
+
+ $cimStartMode = 'Unknown'
+ try {
+ $svcCim = Get-CimInstance Win32_Service -Filter "Name='$svcName'" -ErrorAction SilentlyContinue
+ if ($svcCim) { $cimStartMode = $svcCim.StartMode }
+ } catch { }
+
+ Write-Log -message ("uninstall_appx_packages :: AppXSvc is NOT disabled. Status: {0}, RegStart: {1}, CimStartMode: {2}" -f $status, $regStartStr, $cimStartMode) -severity 'ERROR'
+ exit 2
+ }
+
+ Write-Log -message 'uninstall_appx_packages :: complete (AppXSvc disabled)' -severity 'DEBUG'
+ exit 0
+}
+catch {
+ Write-Log -message ("uninstall_appx_packages :: FATAL: {0}" -f $_.Exception.ToString()) -severity 'ERROR'
+ exit 1
+}
diff --git a/modules/win_disable_services/files/appxpackages/uninstall.ps1 b/modules/win_disable_services/files/appxpackages/uninstall.ps1
index 8cdbf6742..4778d3e8e 100644
--- a/modules/win_disable_services/files/appxpackages/uninstall.ps1
+++ b/modules/win_disable_services/files/appxpackages/uninstall.ps1
@@ -1,269 +1,320 @@
$apps = @{
- "Bing Search" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9nzbf4gt040c"
- "Description" = "Web Search from Microsoft Bing provides web results and answers in Windows Search"
- }
- "Clipchamp.Clipchamp" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9p1j8s7ccwwt?hl=en-us&gl=US"
- "Description" = "Create videos with a few clicks"
- }
- "Microsoft.549981C3F5F10" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/cortana/9NFFX4SZZ23L?hl=en-us&gl=US"
- "Description" = "Cortana (could not update)"
- }
- "Microsoft.BingNews" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-news/9wzdncrfhvfw"
- "Description" = "Microsoft News app"
- }
- "Microsoft.BingWeather" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/msn-weather/9wzdncrfj3q2"
- "Description" = "MSN Weather app"
- }
- "Microsoft.DesktopAppInstaller" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9NBLGGH4NNS1"
- "Description" = "Microsoft App Installer for Windows 10 makes sideloading Windows apps easy"
- }
- "Microsoft.GetHelp" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/customize-get-help-app"
- "Description" = "App that facilitates free support for Microsoft products"
- }
- "Microsoft.Getstarted" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-tips/9wzdncrdtbjj"
- "Description" = "Windows 10 tips app"
- }
- "Microsoft.MicrosoftOfficeHub" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/office/9wzdncrd29v9"
- "Description" = "Office UWP app suite"
- }
- "Microsoft.Office.OneNote" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/onenote-for-windows-10/9wzdncrfhvjl"
- "Description" = "Office UWP OneNote app"
+ "Bing Search" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9nzbf4gt040c"
+ Description = "Web Search from Microsoft Bing provides web results and answers in Windows Search"
}
+
+ "Clipchamp.Clipchamp" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9p1j8s7ccwwt?hl=en-us&gl=US"
+ Description = "Create videos with a few clicks"
+ }
+
+ "Microsoft.549981C3F5F10" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/cortana/9NFFX4SZZ23L?hl=en-us&gl=US"
+ Description = "Cortana (could not update)"
+ }
+
+ "Microsoft.BingNews" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-news/9wzdncrfhvfw"
+ Description = "Microsoft News app"
+ }
+
+ "Microsoft.BingWeather" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/msn-weather/9wzdncrfj3q2"
+ Description = "MSN Weather app"
+ }
+
+ "Microsoft.DesktopAppInstaller" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9NBLGGH4NNS1"
+ Description = "Microsoft App Installer for Windows 10 makes sideloading Windows apps easy"
+ }
+
+ "Microsoft.GetHelp" = @{
+ VDIState = "Unchanged"
+ URL = "https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/customize-get-help-app"
+ Description = "App that facilitates free support for Microsoft products"
+ }
+
+ "Microsoft.Getstarted" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-tips/9wzdncrdtbjj"
+ Description = "Windows 10 tips app"
+ }
+
+ "Microsoft.MicrosoftOfficeHub" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/office/9wzdncrd29v9"
+ Description = "Office UWP app suite"
+ }
+
+ "Microsoft.Office.OneNote" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/onenote-for-windows-10/9wzdncrfhvjl"
+ Description = "Office UWP OneNote app"
+ }
+
"Microsoft.MicrosoftSolitaireCollection" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-solitaire-collection/9wzdncrfhwd2"
- "Description" = "Solitaire suite of games"
- }
- "Microsoft.MicrosoftStickyNotes" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-sticky-notes/9nblggh4qghw"
- "Description" = "Note-taking app"
- }
- "Microsoft.OutlookForWindows" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9NRX63209R7B?hl=en-us&gl=US"
- "Description" = "a best-in-class email experience that is free for anyone with Windows"
- }
- "Microsoft.MSPaint" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/store/detail/paint-3d/9NBLGGH5FV99"
- "Description" = "Paint 3D app (not Classic Paint app)"
- }
- "Microsoft.Paint" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9PCFS5B6T72H"
- "Description" = "Classic Paint app"
- }
- "Microsoft.People" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-people/9nblggh10pg8"
- "Description" = "Contact management app"
- }
- "Microsoft.PowerAutomateDesktop" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://flow.microsoft.com/en-us/desktop/"
- "Description" = "Power Automate Desktop app. Record desktop and web actions in a single flow"
- }
- "Microsoft.ScreenSketch" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/snip-sketch/9mz95kl8mr0l"
- "Description" = "Snip and Sketch app"
- }
- "Microsoft.SkypeApp" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/skype/9wzdncrfj364"
- "Description" = "Instant message, voice or video call app"
- }
- "Microsoft.StorePurchaseApp" = @{
- "VDIState" = "Unchanged"
- "URL" = ""
- "Description" = "Store purchase app helper"
- }
- "Microsoft.Todos" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-to-do-lists-tasks-reminders/9nblggh5r558"
- "Description" = "Microsoft To Do makes it easy to plan your day and manage your life"
- }
- "Microsoft.WinDbg.Fast" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9PGJGD53TN86?hl=en-us&gl=US"
- "Description" = "Microsoft WinDbg"
- }
- "Microsoft.Windows.DevHome" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://learn.microsoft.com/en-us/windows/dev-home/"
- "Description" = "A control center providing the ability to monitor projects in your dashboard using customizable widgets and more"
- }
- "Microsoft.Windows.Photos" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/microsoft-photos/9wzdncrfjbh4"
- "Description" = "Photo and video editor"
- }
- "Microsoft.WindowsAlarms" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-alarms-clock/9wzdncrfj3pr"
- "Description" = "A combination app, of alarm clock, world clock, timer, and stopwatch."
- }
- "Microsoft.WindowsCalculator" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-calculator/9wzdncrfhvn5"
- "Description" = "Microsoft Calculator app"
- }
- "Microsoft.WindowsCamera" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-camera/9wzdncrfjbbg"
- "Description" = "Camera app to manage photos and video"
- }
- "microsoft.windowscommunicationsapps" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/mail-and-calendar/9wzdncrfhvqm"
- "Description" = "Mail & Calendar apps"
- }
- "Microsoft.WindowsFeedbackHub" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/feedback-hub/9nblggh4r32n"
- "Description" = "App to provide Feedback on Windows and apps to Microsoft"
- }
- "Microsoft.WindowsMaps" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-maps/9wzdncrdtbvb"
- "Description" = "Microsoft Maps app"
- }
- "Microsoft.WindowsNotepad" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-notepad/9msmlrh6lzf3"
- "Description" = "Fast, simple text editor for plain text documents and source code files."
- }
- "Microsoft.WindowsStore" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://blogs.windows.com/windowsexperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/"
- "Description" = "Windows Store app"
- }
- "Microsoft.WindowsSoundRecorder" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-voice-recorder/9wzdncrfhwkn"
- "Description" = "(Voice recorder)"
- }
- "Microsoft.WindowsTerminal" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701"
- "Description" = "A terminal app featuring tabs, panes, Unicode, UTF-8 character support, and GPU text rendering engine."
- }
- "Microsoft.Winget.Platform.Source" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://learn.microsoft.com/en-us/windows/package-manager/winget/"
- "Description" = "The Winget tool enables users to manage applications on Win10 and Win11 devices. This tool is the client interface to the Windows Package Manager service"
- }
- "Microsoft.Xbox.TCUI" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://docs.microsoft.com/en-us/gaming/xbox-live/features/general/tcui/live-tcui-overview"
- "Description" = "XBox Title Callable UI (TCUI) enables your game code to call pre-defined user interface displays"
- }
- "Microsoft.XboxIdentityProvider" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/xbox-identity-provider/9wzdncrd1hkw"
- "Description" = "A system app that enables PC games to connect to Xbox Live."
- }
- "Microsoft.XboxSpeechToTextOverlay" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://support.xbox.com/help/account-profile/accessibility/use-game-chat-transcription"
- "Description" = "Xbox game transcription overlay"
- }
- "Microsoft.YourPhone" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/Your-phone/9nmpj99vjbwv"
- "Description" = "Android phone to PC device interface app"
- }
- "Microsoft.ZuneMusic" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/groove-music/9wzdncrfj3pt"
- "Description" = "Groove Music app"
- }
- "Microsoft.ZuneVideo" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/movies-tv/9wzdncrfj3p2"
- "Description" = "Movies and TV app"
- }
- "MicrosoftCorporationII.QuickAssist" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/9P7BP5VNWKX5?hl=en-us&gl=US"
- "Description" = "Microsoft remote help app"
- }
- "MicrosoftWindows.Client.WebExperience" = @{
- "VDIState" = "Unchanged"
- "URL" = ""
- "Description" = "Windows 11 Internet information widget"
- }
- "Microsoft.XboxApp" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/store/apps/9wzdncrfjbd8"
- "Description" = "Xbox 'Console Companion' app (games, friends, etc.)"
- }
- "Microsoft.MixedReality.Portal" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/mixed-reality-portal/9ng1h8b3zc7m"
- "Description" = "The app that facilitates Windows Mixed Reality setup, and serves as the command center for mixed reality experiences"
- }
- "Microsoft.Microsoft3DViewer" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/p/3d-viewer/9nblggh42ths"
- "Description" = "App to view common 3D file types"
- }
- "MicrosoftTeams" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/xp8bt8dw290mpq"
- "Description" = "Microsoft communication platform"
- }
- "MSTeams" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://apps.microsoft.com/detail/xp8bt8dw290mpq"
- "Description" = "Microsoft communication platform"
- }
- "Microsoft.OneDriveSync" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://docs.microsoft.com/en-us/onedrive/one-drive-sync"
- "Description" = "Microsoft OneDrive sync app (included in Office 2016 or later)"
- }
- "Microsoft.Wallet" = @{
- "VDIState" = "Unchanged"
- "URL" = "https://www.microsoft.com/en-us/payments"
- "Description" = "(Microsoft Pay) for Edge browser on certain devices"
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-solitaire-collection/9wzdncrfhwd2"
+ Description = "Solitaire suite of games"
+ }
+
+ "Microsoft.MicrosoftStickyNotes" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-sticky-notes/9nblggh4qghw"
+ Description = "Note-taking app"
+ }
+
+ "Microsoft.OutlookForWindows" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9NRX63209R7B?hl=en-us&gl=US"
+ Description = "A best-in-class email experience that is free for anyone with Windows"
+ }
+
+ "Microsoft.MSPaint" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/store/detail/paint-3d/9NBLGGH5FV99"
+ Description = "Paint 3D app (not Classic Paint app)"
+ }
+
+ "Microsoft.Paint" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9PCFS5B6T72H"
+ Description = "Classic Paint app"
+ }
+
+ "Microsoft.People" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-people/9nblggh10pg8"
+ Description = "Contact management app"
+ }
+
+ "Microsoft.PowerAutomateDesktop" = @{
+ VDIState = "Unchanged"
+ URL = "https://flow.microsoft.com/en-us/desktop/"
+ Description = "Power Automate Desktop app. Record desktop and web actions in a single flow"
+ }
+
+ "Microsoft.ScreenSketch" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/snip-sketch/9mz95kl8mr0l"
+ Description = "Snip and Sketch app"
+ }
+
+ "Microsoft.SkypeApp" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/skype/9wzdncrfj364"
+ Description = "Instant message, voice or video call app"
+ }
+
+ "Microsoft.StorePurchaseApp" = @{
+ VDIState = "Unchanged"
+ URL = ""
+ Description = "Store purchase app helper"
+ }
+
+ "Microsoft.Todos" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-to-do-lists-tasks-reminders/9nblggh5r558"
+ Description = "Microsoft To Do makes it easy to plan your day and manage your life"
+ }
+
+ "Microsoft.WinDbg.Fast" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9PGJGD53TN86?hl=en-us&gl=US"
+ Description = "Microsoft WinDbg"
+ }
+
+ "Microsoft.Windows.DevHome" = @{
+ VDIState = "Unchanged"
+ URL = "https://learn.microsoft.com/en-us/windows/dev-home/"
+ Description = "A control center providing the ability to monitor projects in your dashboard using customizable widgets and more"
+ }
+
+ "Microsoft.Windows.Photos" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/microsoft-photos/9wzdncrfjbh4"
+ Description = "Photo and video editor"
+ }
+
+ "Microsoft.WindowsAlarms" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-alarms-clock/9wzdncrfj3pr"
+ Description = "A combination app of alarm clock, world clock, timer, and stopwatch."
+ }
+
+ "Microsoft.WindowsCalculator" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-calculator/9wzdncrfhvn5"
+ Description = "Microsoft Calculator app"
+ }
+
+ "Microsoft.WindowsCamera" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-camera/9wzdncrfjbbg"
+ Description = "Camera app to manage photos and video"
+ }
+
+ "microsoft.windowscommunicationsapps" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/mail-and-calendar/9wzdncrfhvqm"
+ Description = "Mail & Calendar apps"
+ }
+
+ "Microsoft.WindowsFeedbackHub" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/feedback-hub/9nblggh4r32n"
+ Description = "App to provide Feedback on Windows and apps to Microsoft"
+ }
+
+ "Microsoft.WindowsMaps" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-maps/9wzdncrdtbvb"
+ Description = "Microsoft Maps app"
+ }
+
+ "Microsoft.WindowsNotepad" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-notepad/9msmlrh6lzf3"
+ Description = "Fast, simple text editor for plain text documents and source code files."
+ }
+
+ "Microsoft.WindowsStore" = @{
+ VDIState = "Unchanged"
+ URL = "https://blogs.windows.com/windowsexperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/"
+ Description = "Windows Store app"
+ }
+
+ "Microsoft.WindowsSoundRecorder" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-voice-recorder/9wzdncrfhwkn"
+ Description = "(Voice recorder)"
+ }
+
+ "Microsoft.WindowsTerminal" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701"
+ Description = "A terminal app featuring tabs, panes, Unicode, UTF-8 character support, and a GPU text rendering engine."
+ }
+
+ "Microsoft.Winget.Platform.Source" = @{
+ VDIState = "Unchanged"
+ URL = "https://learn.microsoft.com/en-us/windows/package-manager/winget/"
+ Description = "The Winget tool enables users to manage applications on Win10 and Win11 devices. This tool is the client interface to the Windows Package Manager service"
+ }
+
+ "Microsoft.Xbox.TCUI" = @{
+ VDIState = "Unchanged"
+ URL = "https://docs.microsoft.com/en-us/gaming/xbox-live/features/general/tcui/live-tcui-overview"
+ Description = "XBox Title Callable UI (TCUI) enables your game code to call pre-defined user interface displays"
+ }
+
+ "Microsoft.XboxIdentityProvider" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/xbox-identity-provider/9wzdncrd1hkw"
+ Description = "A system app that enables PC games to connect to Xbox Live."
+ }
+
+ "Microsoft.XboxSpeechToTextOverlay" = @{
+ VDIState = "Unchanged"
+ URL = "https://support.xbox.com/help/account-profile/accessibility/use-game-chat-transcription"
+ Description = "Xbox game transcription overlay"
+ }
+
+ "Microsoft.YourPhone" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/Your-phone/9nmpj99vjbwv"
+ Description = "Android phone to PC device interface app"
+ }
+
+ "Microsoft.ZuneMusic" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/groove-music/9wzdncrfj3pt"
+ Description = "Groove Music app"
+ }
+
+ "Microsoft.ZuneVideo" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/movies-tv/9wzdncrfj3p2"
+ Description = "Movies and TV app"
+ }
+
+ "MicrosoftCorporationII.QuickAssist" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/9P7BP5VNWKX5?hl=en-us&gl=US"
+ Description = "Microsoft remote help app"
+ }
+
+ "MicrosoftWindows.Client.WebExperience" = @{
+ VDIState = "Unchanged"
+ URL = ""
+ Description = "Windows 11 Internet information widget"
+ }
+
+ "Microsoft.XboxApp" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/store/apps/9wzdncrfjbd8"
+ Description = "Xbox 'Console Companion' app (games, friends, etc.)"
+ }
+
+ "Microsoft.MixedReality.Portal" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/mixed-reality-portal/9ng1h8b3zc7m"
+ Description = "The app that facilitates Windows Mixed Reality setup, and serves as the command center for mixed reality experiences"
+ }
+
+ "Microsoft.Microsoft3DViewer" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/p/3d-viewer/9nblggh42ths"
+ Description = "App to view common 3D file types"
+ }
+
+ "MicrosoftTeams" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/xp8bt8dw290mpq"
+ Description = "Microsoft communication platform"
+ }
+
+ "MSTeams" = @{
+ VDIState = "Unchanged"
+ URL = "https://apps.microsoft.com/detail/xp8bt8dw290mpq"
+ Description = "Microsoft communication platform"
+ }
+
+ "Microsoft.OneDriveSync" = @{
+ VDIState = "Unchanged"
+ URL = "https://docs.microsoft.com/en-us/onedrive/one-drive-sync"
+ Description = "Microsoft OneDrive sync app (included in Office 2016 or later)"
+ }
+
+ "Microsoft.Wallet" = @{
+ VDIState = "Unchanged"
+ URL = "https://www.microsoft.com/en-us/payments"
+ Description = "(Microsoft Pay) for Edge browser on certain devices"
}
}
-Foreach ($Key in $apps.Keys) {
+foreach ($Key in $apps.Keys) {
$Item = $apps[$Key]
+
Write-Host "Removing Provisioned Package $Key"
Get-AppxProvisionedPackage -Online |
- Where-Object { $_.PackageName -like ("*{0}*" -f $Key) } |
- Remove-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue | Out-Null
+ Where-Object { $_.PackageName -like ("*{0}*" -f $Key) } |
+ Remove-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
+ Out-Null
Write-Host "Attempting to remove [All Users] $Key - $($Item.Description)"
Get-AppxPackage -AllUsers -Name ("*{0}*" -f $Key) |
- Remove-AppxPackage -AllUsers -ErrorAction SilentlyContinue
+ Remove-AppxPackage -AllUsers -ErrorAction SilentlyContinue
Write-Host "Attempting to remove $Key - $($Item.Description)"
Get-AppxPackage -Name ("*{0}*" -f $Key) |
- Remove-AppxPackage -ErrorAction SilentlyContinue | Out-Null
-
+ Remove-AppxPackage -ErrorAction SilentlyContinue |
+ Out-Null
}
diff --git a/modules/win_disable_services/manifests/disable_defender_smartscreen.pp b/modules/win_disable_services/manifests/disable_defender_smartscreen.pp
new file mode 100644
index 000000000..c21bfe933
--- /dev/null
+++ b/modules/win_disable_services/manifests/disable_defender_smartscreen.pp
@@ -0,0 +1,50 @@
+class win_disable_services::disable_defender_smartscreen {
+
+ ## 1) Shell/Explorer SmartScreen (policy)
+ registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\System':
+ ensure => present,
+ }
+
+ registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\System\EnableSmartScreen':
+ ensure => present,
+ type => dword,
+ data => '0',
+ }
+
+ registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\System\ShellSmartScreenLevel':
+ ensure => absent,
+ }
+
+ ## 2) Explorer non-policy setting (per NinjaOne)
+ registry_key { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer':
+ ensure => present,
+ }
+
+ registry_value { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SmartScreenEnabled':
+ ensure => present,
+ type => string,
+ data => 'Off',
+ }
+
+ ## 3) Edge SmartScreen (official Edge policy)
+ registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Edge':
+ ensure => present,
+ }
+
+ registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Edge\SmartScreenEnabled':
+ ensure => present,
+ type => dword,
+ data => '0',
+ }
+
+ ## 4) Store apps / AppHost web content evaluation
+ registry_key { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost':
+ ensure => present,
+ }
+
+ registry_value { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost\EnableWebContentEvaluation':
+ ensure => present,
+ type => dword,
+ data => '0',
+ }
+}
diff --git a/modules/win_disable_services/manifests/disable_ms_edge.pp b/modules/win_disable_services/manifests/disable_ms_edge.pp
new file mode 100644
index 000000000..fb7a252fb
--- /dev/null
+++ b/modules/win_disable_services/manifests/disable_ms_edge.pp
@@ -0,0 +1,10 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+class win_disable_services::disable_ms_edge {
+ win_disable_services::disable_service { 'edgeupdate':
+ }
+ win_disable_services::disable_service { 'MicrosoftEdgeElevationService':
+ }
+}
diff --git a/modules/win_disable_services/manifests/disable_optional_services.pp b/modules/win_disable_services/manifests/disable_optional_services.pp
new file mode 100644
index 000000000..98b45c358
--- /dev/null
+++ b/modules/win_disable_services/manifests/disable_optional_services.pp
@@ -0,0 +1,78 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+## "- DON'T Disable." = has adverse effects if disabled
+
+class win_disable_services::disable_optional_services {
+
+ $services = [
+
+ # --- Bluetooth ---
+ 'BTAGService', # Bluetooth Audio Gateway Service
+ 'bthserv', # Bluetooth Support Service
+ 'BthAvctpSvc', # AVCTP Service (Bluetooth audio)
+
+ # --- Telemetry / diagnostics ---
+ 'DiagTrack', # Connected User Experiences and Telemetry
+ 'DPS', # Diagnostic Policy Service - Disabled in MaintainSytems script too
+ 'DusmSvc', # Data Usage
+ 'WdiServiceHost', # Diagnostic System Host
+
+ # --- Network discovery & publishing ---
+ 'FDResPub', # Function Discovery Resource Publication
+ 'FDResHost', # Function Discovery Provider Host
+
+ # --- Print / themes / prefetch (optional) ---
+ 'Spooler', # Print Spooler (disable only if you never print)
+ 'Themes', # Themes (visual styles)
+ 'SysMain', # SysMain (SuperFetch / prefetcher)
+
+ # --- Wi-Fi / MS account / notifications / web accounts ---
+ 'WlanSvc', # WLAN AutoConfig (Wi-Fi)
+ 'wlidsvc', # Microsoft Account Sign-in Assistant
+ 'WpnService', # Windows Push Notifications System Service - Disabled in MaintainSytems script too
+ 'TokenBroker', # Web Account Manager
+
+ # --- UWP / Microsoft Store ecosystem ---
+ 'AppReadiness', # App readiness
+# 'AppXSvc', # AppX Deployment Service - won't disable
+# 'CDPSvc', # Connected Devices Platform Service - DON'T Disable.
+# 'ClipSVC', # Client License Service (Store licensing) - DON'T Disable.
+# 'CoreMessagingRegistrar', # CoreMessaging - won't disable
+# 'StateRepository', # State Repository Service - DON'T Disable.
+# 'SystemEventsBroker', # System Events Broker - DON'T Disable.
+# 'TextInputManagementSvc', # Text Input Management - DON'T Disable.
+# 'TimeBrokerSvc', # Time Broker (background tasks) - DON'T Disable.
+
+ # --- Indexing / contacts ---
+ 'TrkWks', # Distributed Link Tracking Client - Disabled in MaintainSytems script too
+
+ # --- Third-party / vendor helpers (excluding nxlog) ---
+ 'igccservice', # Intel Graphics Command Center Service
+ 'IntelAudioService', # Intel Audio Service
+ 'jhi_service', # Intel Dynamic Application Loader Host
+ 'RtkAudioUniversalService', # Realtek Audio Universal Service
+# 'webthreatdefsvc', # Web Threat Defense service - DON'T Disable.
+
+ # --- Others ---
+# 'RmSvc', # Radio Management Service (airplane mode / radios) - DON'T Disable.
+ 'NgcCtnrSvc', # Microsoft Passport Container (Windows Hello / PIN)
+ 'lfsvc', # Geolocation Service
+ 'PcaSvc', # Program Compatibility Assistant Service
+ 'SSDPSRV', # SSDP Discovery/UPnP Discovery
+ ]
+
+ $services_disable_only = [
+ 'webthreatdefsvc',
+ 'RmSvc',
+ ]
+
+ service { $services:
+ enable => false,
+ }
+
+ service { $services_disable_only:
+ enable => false,
+ }
+}
diff --git a/modules/win_disable_services/manifests/disable_sync_from_cloud.pp b/modules/win_disable_services/manifests/disable_sync_from_cloud.pp
new file mode 100644
index 000000000..75ee23477
--- /dev/null
+++ b/modules/win_disable_services/manifests/disable_sync_from_cloud.pp
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+class win_disable_services::disable_sync_from_cloud {
+
+ # GPO: Computer Configuration > Administrative Templates > Windows Components > Sync your settings > Do not sync
+ # Registry: HKLM\SOFTWARE\Policies\Microsoft\Windows\SettingSync
+ # Effect: turns off "Remember my preferences" and none of the preferences are synced. :contentReference[oaicite:1]{index=1}
+
+ registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\SettingSync':
+ ensure => present,
+ }
+
+ # DisableSettingSync: 2 = disable
+ registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\SettingSync\DisableSettingSync':
+ ensure => present,
+ type => dword,
+ data => '2',
+ }
+
+ # DisableSettingSyncUserOverride: 1 = prevent user override (keeps it off)
+ registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\SettingSync\DisableSettingSyncUserOverride':
+ ensure => present,
+ type => dword,
+ data => '1',
+ }
+}
diff --git a/modules/win_disable_services/manifests/uninstall_appx_packages.pp b/modules/win_disable_services/manifests/uninstall_appx_packages.pp
index 8efc57186..063ddbfee 100644
--- a/modules/win_disable_services/manifests/uninstall_appx_packages.pp
+++ b/modules/win_disable_services/manifests/uninstall_appx_packages.pp
@@ -1,8 +1,24 @@
# This class is responsible for disabling AppX packages on Windows.
-class win_disable_services::uninstall_appx_packages {
+class win_disable_services::uninstall_appx_packages (
+ $apx_uninstall
+){
+
+ $ronin_base = $facts['custom_win_roninprogramdata']
+ $script_path = "${ronin_base}\\win_uninstall_appx_packages.ps1"
+
+ file { $script_path:
+ ensure => file,
+ content => file("win_disable_services/appxpackages/${apx_uninstall}"),
+ }
+
exec { 'disable_appx_packages':
- command => file('win_disable_services/appxpackages/uninstall.ps1'),
- provider => powershell,
- timeout => 300,
+ # Call the script file from PowerShell provider
+ command => "& '${script_path}'",
+ provider => powershell,
+ timeout => 300,
+ logoutput => true,
+ returns => [0],
+ require => File[$script_path],
+ tries => 1,
}
}
diff --git a/modules/win_scheduled_tasks/files/BAKmaintainsystem-hw.ps1 b/modules/win_scheduled_tasks/files/BAKmaintainsystem-hw.ps1
new file mode 100644
index 000000000..2f4d49411
--- /dev/null
+++ b/modules/win_scheduled_tasks/files/BAKmaintainsystem-hw.ps1
@@ -0,0 +1,609 @@
+<#
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#>
+
+function Write-Log {
+ param (
+ [string] $message,
+ [string] $severity = 'INFO',
+ [string] $source = 'MaintainSystem',
+ [string] $logName = 'Application'
+ )
+ if (!([Diagnostics.EventLog]::Exists($logName)) -or !([Diagnostics.EventLog]::SourceExists($source))) {
+ New-EventLog -LogName $logName -Source $source
+ }
+ switch ($severity) {
+ 'DEBUG' {
+ $entryType = 'SuccessAudit'
+ $eventId = 2
+ break
+ }
+ 'WARN' {
+ $entryType = 'Warning'
+ $eventId = 3
+ break
+ }
+ 'ERROR' {
+ $entryType = 'Error'
+ $eventId = 4
+ break
+ }
+ default {
+ $entryType = 'Information'
+ $eventId = 1
+ break
+ }
+ }
+ Write-EventLog -LogName $logName -Source $source -EntryType $entryType -Category 0 -EventID $eventId -Message $message
+ if ([Environment]::UserInteractive) {
+ $fc = @{ 'Information' = 'White'; 'Error' = 'Red'; 'Warning' = 'DarkYellow'; 'SuccessAudit' = 'DarkGray' }[$entryType]
+ Write-Host -object $message -ForegroundColor $fc
+ }
+}
+
+function Run-MaintainSystem {
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ #Remove-OldTaskDirectories
+ Get-ChildItem "$env:systemdrive\logs\old" -Recurse -File | Where-Object CreationTime -lt (Get-Date).AddDays(-7) | Remove-Item -Force
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+function CompareConfigBasic {
+ param (
+ [string]$yaml_url = "https://raw.githubusercontent.com/mozilla-platform-ops/worker-images/refs/heads/main/provisioners/windows/MDC1Windows/pools.yml",
+ [string]$PAT
+ )
+
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+
+ process {
+ $yaml = $null
+ $SETPXE = $false
+ $yamlHash = $null
+ $IPAddress = $null
+
+ $Ethernet = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() |
+ Where-Object { $_.Name -match "ethernet" }
+
+ try {
+ $IPAddress = ($Ethernet.GetIPProperties().UnicastAddresses |
+ Where-Object { $_.Address.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork -and $_.Address.IPAddressToString -ne "127.0.0.1" } |
+ Select-Object -First 1 -ExpandProperty Address).IPAddressToString
+ }
+ catch {
+ try {
+ $NetshOutput = netsh interface ip show addresses
+ $IPAddress = ($NetshOutput -match "IP Address" | ForEach-Object {
+ if ($_ -notmatch "127.0.0.1") { $_ -replace ".*?:\s*", "" }
+ })[0]
+ }
+ catch {
+ Write-Log -message "Failed to get IP address" -severity 'ERROR'
+ }
+ }
+
+ if (-not $IPAddress) {
+ Write-Log -message "No IP Address could be determined." -severity 'ERROR'
+ Restart-Computer -Force
+ return
+ }
+
+ Write-Log -message "IP Address: $IPAddress" -severity 'INFO'
+
+ try {
+ $ResolvedName = (Resolve-DnsName -Name $IPAddress -Server "10.48.75.120").NameHost
+ }
+ catch {
+ Write-Log -message "DNS resolution failed." -severity 'ERROR'
+ Restart-Computer -Force
+ return
+ }
+
+ Write-Log -message "Resolved Name: $ResolvedName" -severity 'INFO'
+
+ $index = $ResolvedName.IndexOf('.')
+ if ($index -lt 0) {
+ Write-Log -message "Invalid hostname format." -severity 'ERROR'
+ Restart-Computer -Force
+ return
+ }
+
+ $worker_node_name = $ResolvedName.Substring(0, $index)
+ Write-Log -message "Host name set to: $worker_node_name" -severity 'INFO'
+
+ $localHash = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).GITHASH
+ $localPool = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).worker_pool_id
+
+ $maxRetries = 5
+ $retryDelay = 10
+ $attempt = 0
+ $success = $false
+
+ while ($attempt -lt $maxRetries -and -not $success) {
+ try {
+ $Headers = @{
+ Accept = "application/vnd.github+json"
+ Authorization = "Bearer $($PAT)"
+ "X-GitHub-Api-Version" = "2022-11-28"
+ }
+ $response = Invoke-WebRequest -Uri $yaml_url -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop -Headers $Headers
+ $yaml = $response.Content | ConvertFrom-Yaml
+
+ if ($yaml) {
+ $success = $true
+ }
+ else {
+ throw "YAML content empty"
+ }
+ }
+ catch {
+ Write-Log -message "Attempt $($attempt + 1): Failed to fetch YAML - $_" -severity 'WARN'
+ Start-Sleep -Seconds $retryDelay
+ $attempt++
+ }
+ }
+
+ if (-not $success) {
+ Write-Log -message "YAML could not be loaded. Forcing PXE + reboot." -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
+ }
+
+ $found = $false
+ foreach ($pool in $yaml.pools) {
+ if ($pool.nodes -contains $worker_node_name) {
+ $WorkerPool = $pool.name
+ $yamlHash = $pool.hash
+ $yamlImageName = $pool.image
+ $yamlImageDir = "D:\" + $yamlImageName
+ $found = $true
+ break
+ }
+ }
+
+ if (-not $found) {
+ Write-Log -message "Node not found in YAML. Forcing PXE + reboot." -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
+ }
+
+ Write-Log -message "=== Configuration Comparison ===" -severity 'INFO'
+
+ if ($localPool -ne $WorkerPool) {
+ Write-Log -message "Worker Pool MISMATCH!" -severity 'ERROR'
+ $SETPXE = $true
+ }
+ else {
+ Write-Log -message "Worker Pool Match: $WorkerPool" -severity 'INFO'
+ }
+
+ if ([string]::IsNullOrWhiteSpace($yamlHash) -or $localHash -ne $yamlHash) {
+ Write-Log -message "Git Hash MISMATCH or missing YAML hash!" -severity 'ERROR'
+ Write-Log -message "Local: $localHash" -severity 'WARN'
+ Write-Log -message "YAML : $yamlHash" -severity 'WARN'
+ $SETPXE = $true
+ }
+ else {
+ Write-Log -message "Git Hash Match: $yamlHash" -severity 'INFO'
+ }
+
+ if (!(Test-Path $yamlImageDir)) {
+ Write-Log -message "Image directory missing: $yamlImageDir" -severity 'ERROR'
+ $SETPXE = $true
+ }
+
+ if ($SETPXE) {
+ Write-Log -message "Configuration mismatch — initiating PXE + reboot." -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
+ }
+
+ Write-Log -message "Configuration is correct. No reboot required." -severity 'INFO'
+ }
+
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+function Remove-OldTaskDirectories {
+ param (
+ [string[]] $targets = @('Z:\task_*', 'C:\Users\task_*')
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ foreach ($target in ($targets | Where-Object { (Test-Path -Path ('{0}:\' -f $_[0]) -ErrorAction SilentlyContinue) })) {
+ $all_task_paths = @(Get-ChildItem -Path $target | Sort-Object -Property { $_.LastWriteTime })
+ if ($all_task_paths.length -gt 1) {
+ Write-Log -message ('{0} :: {1} task directories detected matching pattern: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths.length, $target) -severity 'INFO'
+ $old_task_paths = $all_task_paths[0..($all_task_paths.Length - 2)]
+ foreach ($old_task_path in $old_task_paths) {
+ try {
+ & takeown.exe @('/a', '/f', $old_task_path, '/r', '/d', 'Y')
+ & icacls.exe @($old_task_path, '/grant', 'Administrators:F', '/t')
+ Remove-Item -Path $old_task_path -Force -Recurse
+ Write-Log -message ('{0} :: removed task directory: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime) -severity 'INFO'
+ }
+ catch {
+ Write-Log -message ('{0} :: failed to remove task directory: {1}, with last write time: {2}. {3}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime, $_.Exception.Message) -severity 'ERROR'
+ }
+ }
+ }
+ elseif ($all_task_paths.length -eq 1) {
+ Write-Log -message ('{0} :: a single task directory was detected at: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths[0].FullName, $all_task_paths[0].LastWriteTime) -severity 'DEBUG'
+ }
+ else {
+ Write-Log -message ('{0} :: no task directories detected matching pattern: {1}' -f $($MyInvocation.MyCommand.Name), $target) -severity 'DEBUG'
+ }
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+function Check-RoninNodeOptions {
+ param (
+ [string] $inmutable = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").inmutable,
+ [string] $flagfile = "$env:programdata\PuppetLabs\ronin\semaphore\task-claim-state.valid"
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ Write-Host $inmutable
+ if ($inmutable -eq 'true') {
+ Write-Log -message ('{0} :: Node is set to be inmutable' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Remove-Item -path $lock -ErrorAction SilentlyContinue
+ write-host New-item -path $flagfile
+ Exit-PSSession
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+
+Function UpdateRonin {
+ param (
+ [string] $sourceOrg,
+ [string] $sourceRepo,
+ [string] $sourceBranch,
+ [string] $ronin_repo = "$env:systemdrive\ronin"
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ $sourceOrg = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Organisation' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Organisation').Organisation } else { 'mozilla-platform-ops' })
+ $sourceRepo = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Repository' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Repository').Repository } else { 'ronin_puppet' })
+ $sourceBranch = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Branch' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Branch').Branch } else { 'master' })
+
+ Set-Location $ronin_repo
+ git config --global --add safe.directory "C:/ronin"
+ git pull https://github.com/$sourceOrg/$sourceRepo $sourceBranch
+ $git_exit = $LastExitCode
+ if ($git_exit -eq 0) {
+ $git_hash = (git rev-parse --verify HEAD)
+ Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name githash -type string -value $git_hash
+ Write-Log -message ('{0} :: Checking/pulling updates from https://github.com/{1}/{2}. Branch: {3}.' -f $($MyInvocation.MyCommand.Name), ($sourceOrg), ($sourceRepo), ($sourceRev)) -severity 'DEBUG'
+ }
+ else {
+ # Fall back to clone if pull fails
+ Write-Log -message ('{0} :: Git pull failed! https://github.com/{1}/{2}. Branch: {3}.' -f $($MyInvocation.MyCommand.Name), ($sourceOrg), ($sourceRepo), ($sourceRev)) -severity 'DEBUG'
+ Write-Log -message ('{0} :: Deleting old repository and cloning repository .' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Move-item -Path $ronin_repo\manifests\nodes.pp -Destination $env:TEMP\nodes.pp
+ Move-item -Path $ronin_repo\data\secrets\vault.yaml -Destination $env:TEMP\vault.yaml
+ #Remove-Item -Recurse -Force $ronin_repo
+ Start-Sleep -s 2
+ git clone --single-branch --branch $sourceRev https://github.com/$sourceOrg/$sourceRepo $ronin_repo
+ Move-item -Path $env:TEMP\nodes.pp -Destination $ronin_repo\manifests\nodes.pp
+ Move-item -Path $env:TEMP\vault.yaml -Destination $ronin_repo\data\secrets\vault.yaml
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+function Puppet-Run {
+ param (
+ [int] $exit,
+ [string] $lock = "$env:programdata\PuppetLabs\ronin\semaphore\ronin_run.lock",
+ [int] $last_exit = (Get-ItemProperty "HKLM:\SOFTWARE\Mozilla\ronin_puppet").last_run_exit,
+ [string] $run_to_success = (Get-ItemProperty "HKLM:\SOFTWARE\Mozilla\ronin_puppet").runtosuccess,
+ [string] $nodes_def = "$env:systemdrive\ronin\manifests\nodes\odes.pp",
+ [string] $logdir = "$env:systemdrive\logs",
+ [string] $fail_dir = "$env:systemdrive\fail_logs",
+ [string] $log_file = "$datetime-puppetrun.log",
+ [string] $datetime = (get-date -format yyyyMMdd-HHmm),
+ [string] $flagfile = "$env:programdata\PuppetLabs\ronin\semaphore\task-claim-state.valid"
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+
+ Check-RoninNodeOptions
+ UpdateRonin
+
+ # Setting Env variabes for PuppetFile install and Puppet run
+ # The ssl variables are needed for R10k
+ Write-Log -message ('{0} :: Setting Puppet enviroment.' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ $env:path = "$($env:ProgramFiles)\Puppet Labs\Puppet\puppet\bin;$($env:ProgramFiles)\Puppet Labs\Puppet\bin;$env:path"
+
+ $env:SSL_CERT_FILE = "$($env:ProgramFiles)\Puppet Labs\Puppet\puppet\ssl\cert.pem"
+ $env:SSL_CERT_DIR = "$($env:ProgramFiles)\Puppet Labs\Puppet\puppet\ssl"
+ $env:FACTER_env_windows_installdir = "$($env:ProgramFiles)\Puppet Labs\Puppet"
+ $env:PL_BASEDIR = "$($env:ProgramFiles)\Puppet Labs\Puppet"
+ $env:PUPPET_DIR = "$($env:ProgramFiles)\Puppet Labs\Puppet"
+ $env:RUBYLIB = "$($env:ProgramFiles)\Puppet Labs\Puppet\lib"
+
+ $env:USERNAME = "Administrator"
+ $env:USERPROFILE = "$env:systemdrive\Users\Administrator"
+
+ # This is temporary and should be removed after the cloud_windows branch is merged
+ # Hiera lookups will fail after the merge if this is not in place following the merge
+ <#
+ if((test-path $env:systemdrive\ronin\win_hiera.yaml)) {
+ $hiera = "win_hiera.yaml"
+ } else {
+ $hiera = "hiera.yaml"
+ }
+ #>
+ # this will break Win 10 1803 if this is merged into the master brnach
+ $hiera = "hiera.yaml"
+
+ # Needs to be removed from path or a wrong puppet file will be used
+ $env:path = ($env:path.Split(';') | Where-Object { $_ -ne "$env:programfiles\Puppet Labs\Puppet\puppet\bin" }) -join ';'
+ If (!(test-path $fail_dir)) {
+ New-Item -ItemType Directory -Force -Path $fail_dir
+ }
+ Get-ChildItem -Path $logdir\*.log -Recurse | Move-Item -Destination $logdir\old -ErrorAction SilentlyContinue
+ Write-Log -message ('{0} :: Initiating Puppet apply .' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ puppet apply manifests\nodes.pp --onetime --verbose --no-daemonize --no-usecacheonfailure --detailed-exitcodes --no-splay --show_diff --modulepath=modules`;r10k_modules --hiera_config=$hiera --logdest $logdir\$log_file
+ [int]$puppet_exit = $LastExitCode
+
+ if ($run_to_success -eq 'true') {
+ if (($puppet_exit -ne 0) -and ($puppet_exit -ne 2)) {
+ if ($last_exit -eq 0) {
+ Write-Log -message ('{0} :: Puppet apply failed.' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
+ Remove-Item $lock -ErrorAction SilentlyContinue
+ # If the Puppet run fails send logs to papertrail
+ # Nxlog watches $fail_dir for files names *-puppetrun.log
+ Move-Item $logdir\$log_file -Destination $fail_dir
+ shutdown @('-r', '-t', '0', '-c', 'Reboot; Puppet apply failed', '-f', '-d', '4:5')
+ }
+ elseif ($last_exit -ne 0) {
+ Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
+ Remove-Item $lock
+ Move-Item $logdir\$log_file -Destination $fail_dir
+ Write-Log -message ('{0} :: Puppet apply failed. Waiting 10 minutes beofre Reboot' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Start-Sleep 600
+ shutdown @('-r', '-t', '0', '-c', 'Reboot; Puppet apply failed', '-f', '-d', '4:5')
+ }
+ }
+ elseif (($puppet_exit -match 0) -or ($puppet_exit -match 2)) {
+ Write-Log -message ('{0} :: Puppet apply successful' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
+ Remove-Item -path $lock
+ New-item -path $flagfile
+ }
+ else {
+ Write-Log -message ('{0} :: Unable to detrimine state post Puppet apply' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $last_exit
+ Move-Item $logdir\$log_file -Destination $fail_dir
+ Remove-Item -path $lock
+ shutdown @('-r', '-t', '600', '-c', 'Reboot; Unveriable state', '-f', '-d', '4:5')
+ }
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+
+function StartWorkerRunner {
+ param (
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ ## Checking for issues with the user profile.
+ $lastBootTime = Get-WinEvent -LogName "System" -FilterXPath "" |
+ Select-Object -First 1 |
+ ForEach-Object { $_.TimeCreated }
+ $eventIDs = @(1511, 1515)
+
+ $events = Get-WinEvent -LogName "Application" |
+ Where-Object { $_.ID -in $eventIDs -and $_.TimeCreated -gt $lastBootTime } |
+ Sort-Object TimeCreated -Descending | Select-Object -First 1
+
+ if ($events) {
+ Write-Log -message ('{0} :: Possible User Profile Corruption. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Restart-Computer -Force
+ exit
+ }
+ Start-Service -Name worker-runner
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+
+function Get-LoggedInUser {
+ [CmdletBinding()]
+ param (
+
+ )
+
+ @(((query user) -replace '\s{20,39}', ',,') -replace '\s{2,}', ',' | ConvertFrom-Csv)
+}
+
+function Get-LatestGoogleChrome {
+ [CmdletBinding()]
+ param (
+ [String]
+ $Package = "googlechrome"
+ )
+
+ ## Current version of google chrome
+ $current_version = choco list --exact $Package --limit-output | ConvertFrom-Csv -Delimiter '|' -Header 'Name', 'CurrentVersion'
+
+ ## Use chocolatey with outdated
+ $choco_packages = choco outdated --limit-output | ConvertFrom-Csv -Delimiter '|' -Header 'Name', 'CurrentVersion', 'AvailableVersion', 'Pinned'
+
+ ## Check if Google Chrome is present
+ $pkg = $choco_packages | Where-Object { $_.Name -eq $Package }
+
+ ## There is no google chrome update, so output the current version
+ if ([String]::IsNullOrEmpty($pkg)) {
+ Write-Log -message ('{0} :: Google Chrome version installed is {1}' -f $($MyInvocation.MyCommand.Name), $current_version.CurrentVersion) -severity 'DEBUG'
+ }
+ else {
+ ## Chrome is installed and needs to be updated
+ if ($pkg.CurrentVersion -ne $pkg.AvailableVersion) {
+ ## run choco upgrade
+ Write-Log -message ('{0} :: Updating Google Chrome from current: {1} to available: {2}' -f $($MyInvocation.MyCommand.Name), $pkg.currentVersion, $pkg.availableVersion) -severity 'DEBUG'
+ choco upgrade $Package -y "--ignore-checksums" "--ignore-package-exit-codes" "--log-file" $env:systemdrive\logs\googlechrome.log
+ if ($LASTEXITCODE -ne 0) {
+ ## output to papertrail
+ Write-Log -message ('{0} :: choco upgrade googlechrome failed with {1}' -f $($MyInvocation.MyCommand.Name), $LASTEXITCODE) -severity 'DEBUG'
+ ## output chocolatey logs to papertrail
+ Get-Content $env:systemdrive\logs\googlechrome.log | ForEach-Object { Write-Log -message $_ -severity 'DEBUG' }
+ ## Sending the logs to papertrail, wait 30 seconds
+ Start-Sleep -Seconds 60
+ ## PXE Boot
+ Set-PXE
+ }
+ else {
+ ## Need to reboot in order to complete the upgrade
+ Write-Log -message ('{0} :: Google Chrome needs to reboot to complete upgrade. Rebooting..' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Start-Sleep -Seconds 10
+ Restart-Computer -Force
+ }
+ }
+ }
+}
+
+function Set-PXE {
+ param (
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ $temp_dir = "$env:systemdrive\temp\"
+ New-Item -ItemType Directory -Force -Path $temp_dir -ErrorAction SilentlyContinue
+
+ bcdedit /enum firmware > $temp_dir\firmware.txt
+
+ $fwbootmgr = Select-String -Path "$temp_dir\firmware.txt" -Pattern "{fwbootmgr}"
+ if (!$fwbootmgr) {
+ Write-Log -message ('{0} :: Device is configured for Legacy Boot. Exiting!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Exit 999
+ }
+ Try {
+ # Get the line of text with the GUID for the PXE boot option.
+ # IPV4 = most PXE boot options
+ $FullLine = (( Get-Content $temp_dir\firmware.txt | Select-String "IPV4|EFI Network" -Context 1 -ErrorAction Stop ).context.precontext)[0]
+
+ # Remove all text but the GUID
+ $GUID = '{' + $FullLine.split('{')[1]
+
+ # Add the PXE boot option to the top of the boot order on next boot
+ bcdedit /set "{fwbootmgr}" bootsequence "$GUID"
+
+ Write-Log -message ('{0} :: Device will PXE boot. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Restart-Computer -Force
+ }
+ Catch {
+ Write-Log -message ('{0} :: Unable to set next boot to PXE. Exiting!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Exit 888
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+
+function Test-ConnectionUntilOnline {
+ param (
+ [string]$Hostname = "www.google.com",
+ [int]$Interval = 5,
+ [int]$TotalTime = 120
+ )
+
+ $elapsedTime = 0
+
+ while ($elapsedTime -lt $totalTime) {
+ if (Test-Connection -ComputerName $hostname -Count 1 -Quiet) {
+ Write-Log -message ('{0} :: {1} is online! Continuing.' -f $($MyInvocation.MyCommand.Name), $ENV:COMPUTERNAME) -severity 'DEBUG'
+ return
+ }
+ else {
+ Write-Log -message ('{0} :: {1} is not online, checking again in {2}' -f $($MyInvocation.MyCommand.Name), $ENV:COMPUTERNAME, $interval) -severity 'DEBUG'
+ Start-Sleep -Seconds $interval
+ $elapsedTime += $interval
+ }
+ }
+
+ Write-Log -message ('{0} :: {1} did not come online within {2} seconds' -f $($MyInvocation.MyCommand.Name), $ENV:COMPUTERNAME, $totalTime) -severity 'DEBUG'
+ throw "Connection timeout."
+}
+
+## Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1910123
+## The bug tracks when we reimaged a machine and the machine had a different refresh rate (64hz vs 60hz)
+## This next line will check if the refresh rate is not 60hz and trigger a reimage if so
+$hardware = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -Property Manufacturer, Model
+$model = $hardware.Model
+$refresh_rate = (Get-WmiObject win32_videocontroller).CurrentRefreshRate
+if ($refresh_rate -ne "60") {
+ Write-Log -message ('{0} :: Refresh rate is {1}. Reimaging {2}' -f $($MyInvocation.MyCommand.Name), $refresh_rate, $ENV:COMPUTERNAME) -severity 'DEBUG'
+ Set-PXE
+}
+
+$bootstrap_stage = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").bootstrap_stage
+If ($bootstrap_stage -eq 'complete') {
+ CompareConfigBasic
+ Start-Sleep -Seconds 2
+ Run-MaintainSystem
+ ## We're getting user profile corruption errors, so let's check that the user is logged in using quser.exe
+ for ($i = 0; $i -lt 3; $i++) {
+ $loggedInUser = (Get-LoggedInUser).UserName -replace ">"
+ if ($loggedInUser -notmatch "task") {
+ Write-Log -message ('{0} :: User logged in: {1}' -f $($MyInvocation.MyCommand.Name), $loggedInUser) -severity 'DEBUG'
+ Start-Sleep -Seconds 10
+ }
+ else {
+ Write-Log -message ('{0} :: User logged in: {1}' -f $($MyInvocation.MyCommand.Name), $loggedInUser) -severity 'DEBUG'
+ break
+ }
+ }
+
+ ## Let's make sure the machine is online before checking the internet
+ Test-ConnectionUntilOnline
+
+ ## Let's check for the latest install of google chrome using chocolatey before starting worker runner
+ ## Instead of querying chocolatey each time this runs, let's query chrome json endoint and check locally installed version
+ Get-LatestGoogleChrome
+
+ StartWorkerRunner
+}
+else {
+ Write-Log -message ('{0} :: Bootstrap has not completed. EXITING!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Exit-PSSession
+}
diff --git a/modules/win_scheduled_tasks/files/at_task_user_logon.ps1 b/modules/win_scheduled_tasks/files/at_task_user_logon.ps1
index 114302967..34a43ef4e 100644
--- a/modules/win_scheduled_tasks/files/at_task_user_logon.ps1
+++ b/modules/win_scheduled_tasks/files/at_task_user_logon.ps1
@@ -46,6 +46,499 @@ function Write-Log {
}
}
+function Remove-OneDriveScheduledTasks {
+ [CmdletBinding()]
+ param(
+ [int]$TimeoutSeconds = 180,
+ [int]$RetryIntervalSeconds = 10,
+ [int]$PerTaskDeleteTimeoutSeconds = 60,
+ [int]$PerTaskRetryIntervalSeconds = 3
+ )
+ ## give it a minute to for schd task to be available
+ start-sleep -s 60
+ function Get-OneDriveTaskNames {
+ try {
+ $rows = @(schtasks.exe /Query /FO CSV /V 2>$null | ConvertFrom-Csv)
+ if (-not $rows -or $rows.Count -eq 0) { return @() }
+
+ $matches = $rows | Where-Object {
+ ($_.TaskName -match '(?i)onedrive') -or
+ (($_.'Task To Run') -and (($_.'Task To Run') -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.Actions) -and ($_.Actions -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.'Task Run') -and (($_.'Task Run') -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.Actions) -and ($_.Actions -match '(?i)OneDriveSetup\.exe|\\OneDrive\.exe')) -or
+ (($_.'Task To Run') -and (($_.'Task To Run') -match '(?i)OneDriveSetup\.exe|\\OneDrive\.exe'))
+ }
+
+ return @($matches | Select-Object -ExpandProperty TaskName -Unique)
+ }
+ catch {
+ Write-Log -message ("OneDriveTasks :: enumerate failed: {0}" -f $_.Exception.Message) -severity 'WARN'
+ return @()
+ }
+ }
+
+ function Test-TaskExists([string]$TaskName) {
+ try {
+ schtasks.exe /Query /TN "$TaskName" 1>$null 2>$null
+ return ($LASTEXITCODE -eq 0)
+ } catch {
+ return $true # assume it exists if we couldn't query
+ }
+ }
+
+ function Remove-TaskWithRetries {
+ param(
+ [Parameter(Mandatory)][string]$TaskName
+ )
+
+ $deadline = (Get-Date).AddSeconds($PerTaskDeleteTimeoutSeconds)
+ $attempt = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $attempt++
+
+ try {
+ schtasks.exe /Delete /TN "$TaskName" /F 2>$null | Out-Null
+ $exit = $LASTEXITCODE
+
+ if ($exit -eq 0) {
+ # Some tasks "delete" but linger briefly; verify
+ if (-not (Test-TaskExists -TaskName $TaskName)) {
+ Write-Log -message ("OneDriveTasks :: deleted {0} (attempt {1})" -f $TaskName, $attempt) -severity 'INFO'
+ return $true
+ }
+
+ Write-Log -message ("OneDriveTasks :: delete reported success but task still exists: {0} (attempt {1})" -f $TaskName, $attempt) -severity 'WARN'
+ } else {
+ Write-Log -message ("OneDriveTasks :: delete failed {0} (exit {1}, attempt {2})" -f $TaskName, $exit, $attempt) -severity 'WARN'
+ }
+ }
+ catch {
+ Write-Log -message ("OneDriveTasks :: exception deleting {0} (attempt {1}): {2}" -f $TaskName, $attempt, $_.Exception.Message) -severity 'WARN'
+ }
+
+ Start-Sleep -Seconds $PerTaskRetryIntervalSeconds
+ }
+
+ Write-Log -message ("OneDriveTasks :: timeout deleting {0} after {1}s" -f $TaskName, $PerTaskDeleteTimeoutSeconds) -severity 'ERROR'
+ return $false
+ }
+
+ Write-Log -message ("OneDriveTasks :: begin (timeout={0}s, interval={1}s, perTaskTimeout={2}s)" -f $TimeoutSeconds, $RetryIntervalSeconds, $PerTaskDeleteTimeoutSeconds) -severity 'DEBUG'
+
+ $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
+ $pass = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $pass++
+ $targets = Get-OneDriveTaskNames
+
+ if (-not $targets -or $targets.Count -eq 0) {
+ Write-Log -message ("OneDriveTasks :: none found (pass {0})" -f $pass) -severity 'INFO'
+ Write-Log -message "OneDriveTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ Write-Log -message ("OneDriveTasks :: pass {0}: found {1} task(s)" -f $pass, $targets.Count) -severity 'INFO'
+
+ foreach ($tn in $targets) {
+ $null = Remove-TaskWithRetries -TaskName $tn
+ }
+
+ # Re-check right away; if still present, sleep and retry until overall timeout
+ $stillThere = Get-OneDriveTaskNames
+ if (-not $stillThere -or $stillThere.Count -eq 0) {
+ Write-Log -message ("OneDriveTasks :: verification OK after pass {0}" -f $pass) -severity 'INFO'
+ Write-Log -message "OneDriveTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ $remaining = [math]::Max(0, [int]($deadline - (Get-Date)).TotalSeconds)
+ Write-Log -message ("OneDriveTasks :: still present after pass {0} (remaining {1}s). Sleeping {2}s..." -f $pass, $remaining, $RetryIntervalSeconds) -severity 'WARN'
+ Start-Sleep -Seconds $RetryIntervalSeconds
+ }
+
+ $final = Get-OneDriveTaskNames
+ if ($final -and $final.Count -gt 0) {
+ $sample = ($final | Select-Object -First 10) -join '; '
+ Write-Log -message ("OneDriveTasks :: timeout after {0}s. Remaining task(s): {1}" -f $TimeoutSeconds, $sample) -severity 'ERROR'
+ } else {
+ Write-Log -message "OneDriveTasks :: end (success at timeout boundary)" -severity 'INFO'
+ }
+}
+
+function Disable-OneDriveBackupPopup {
+ [CmdletBinding()]
+ param()
+
+ Write-Log -message "Disable-OneDriveBackupPopup :: begin" -severity 'INFO'
+
+ try {
+ $wb = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsBackup'
+ New-Item -Path $wb -Force | Out-Null
+ New-ItemProperty -Path $wb -Name 'DisableMonitoring' -PropertyType DWord -Value 1 -Force | Out-Null
+ Write-Log -message "Disable-OneDriveBackupPopup :: Set WindowsBackup DisableMonitoring=1" -severity 'INFO'
+ } catch {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Failed setting DisableMonitoring: {0}" -f $_.Exception.Message) -severity 'WARN'
+ }
+
+ try {
+ $odPol = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OneDrive'
+ New-Item -Path $odPol -Force | Out-Null
+ New-ItemProperty -Path $odPol -Name 'DisableFileSyncNGSC' -PropertyType DWord -Value 1 -Force | Out-Null
+ Write-Log -message "Disable-OneDriveBackupPopup :: Set OneDrive DisableFileSyncNGSC=1" -severity 'INFO'
+ } catch {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Failed setting DisableFileSyncNGSC: {0}" -f $_.Exception.Message) -severity 'WARN'
+ }
+
+ try {
+ Get-Process -Name OneDrive -ErrorAction SilentlyContinue | ForEach-Object {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Stopping OneDrive.exe (Id={0})" -f $_.Id) -severity 'INFO'
+ Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
+ }
+ } catch {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Failed stopping OneDrive process: {0}" -f $_.Exception.Message) -severity 'WARN'
+ }
+
+ function Remove-RunEntry([string]$HiveRoot) {
+ $runKey = "${HiveRoot}\Software\Microsoft\Windows\CurrentVersion\Run"
+ foreach ($name in @('OneDrive','OneDriveSetup','Microsoft OneDrive')) {
+ try {
+ & reg.exe delete $runKey /v $name /f 1>$null 2>$null
+ } catch { }
+ }
+ }
+
+ $defaultNtUser = 'C:\Users\Default\NTUSER.DAT'
+ if (Test-Path $defaultNtUser) {
+ try {
+ & reg.exe load 'HKU\DefaultUser' $defaultNtUser 1>$null 2>$null
+ Remove-RunEntry 'HKU\DefaultUser'
+ & reg.exe unload 'HKU\DefaultUser' 1>$null 2>$null
+ Write-Log -message "Disable-OneDriveBackupPopup :: Cleared OneDrive Run entries in Default user profile" -severity 'INFO'
+ } catch {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Failed editing Default user hive: {0}" -f $_.Exception.Message) -severity 'WARN'
+ try { & reg.exe unload 'HKU\DefaultUser' 1>$null 2>$null } catch { }
+ }
+ } else {
+ Write-Log -message "Disable-OneDriveBackupPopup :: Default NTUSER.DAT not found; skipping default profile edit" -severity 'DEBUG'
+ }
+
+ try {
+ $userSids = @(Get-ChildItem Registry::HKEY_USERS -ErrorAction SilentlyContinue |
+ Where-Object { $_.PSChildName -match '^S-1-5-21-' } |
+ Select-Object -ExpandProperty PSChildName)
+
+ foreach ($sid in $userSids) {
+ Remove-RunEntry ("HKU\{0}" -f $sid)
+ }
+
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Cleared OneDrive Run entries in {0} loaded user hive(s)" -f $userSids.Count) -severity 'INFO'
+ } catch {
+ Write-Log -message ("Disable-OneDriveBackupPopup :: Failed clearing loaded user hives: {0}" -f $_.Exception.Message) -severity 'WARN'
+ }
+
+ Write-Log -message "Disable-OneDriveBackupPopup :: complete (recommend reboot)" -severity 'INFO'
+}
+
+function Disable-PerUserUwpServices {
+ [CmdletBinding()]
+ param (
+ [string[]]
+ $ServicePrefixes = @(
+ 'cbdhsvc_', # Clipboard User Service
+ 'OneSyncSvc_', # Sync Host
+ 'UdkUserSvc_', # Udk User Service
+ 'PimIndexMaintenanceSvc_', # Contact/People indexing
+ 'UnistoreSvc_', # User Data Storage
+ 'UserDataSvc_', # User Data Access
+ 'CDPUserSvc_', # Connected Devices Platform (user)
+ 'WpnUserService_', # Push Notifications (user)
+ 'webthreatdefusersvc_' # Web Threat Defense (user)
+ )
+ )
+
+ foreach ($prefix in $ServicePrefixes) {
+
+ $svcList = Get-Service -Name "$prefix*" -ErrorAction SilentlyContinue
+
+ if (-not $svcList) {
+ Write-Log -message ('{0} :: No services found for prefix {1}' -f $($MyInvocation.MyCommand.Name), $prefix) -severity 'DEBUG'
+ continue
+ }
+
+ foreach ($svc in $svcList) {
+ try {
+ if ($svc.Status -eq 'Running') {
+ Write-Log -message ('{0} :: Stopping per-user service {1}' -f $($MyInvocation.MyCommand.Name), $svc.Name) -severity 'DEBUG'
+ Stop-Service -Name $svc.Name -Force -ErrorAction Stop
+ }
+ else {
+ Write-Log -message ('{0} :: Service {1} is already {2}, no action needed' -f $($MyInvocation.MyCommand.Name), $svc.Name, $svc.Status) -severity 'DEBUG'
+ }
+ }
+ catch {
+ Write-Log -message ('{0} :: Failed to stop service {1}: {2}' -f $($MyInvocation.MyCommand.Name), $svc.Name, $_.Exception.Message) -severity 'DEBUG'
+ }
+ }
+ }
+}
+
+function Remove-EdgeScheduledTasks {
+ [CmdletBinding()]
+ param(
+ [int]$TimeoutSeconds = 180,
+ [int]$RetryIntervalSeconds = 10,
+ [int]$PerTaskDeleteTimeoutSeconds = 60,
+ [int]$PerTaskRetryIntervalSeconds = 3
+ )
+
+ # Match only the common Edge updater tasks (keeps this "safe-simple")
+ $NamePatterns = @(
+ '(?i)\\MicrosoftEdgeUpdateTaskMachineCore',
+ '(?i)\\MicrosoftEdgeUpdateTaskMachineUA',
+ '(?i)\\MicrosoftEdgeUpdateTaskMachine', # some builds vary
+ '(?i)\\EdgeUpdate' # fallback
+ )
+
+ $ActionPatterns = @(
+ '(?i)msedgeupdate\.exe',
+ '(?i)microsoftedgeupdate\.exe',
+ '(?i)edgeupdate\.exe'
+ )
+
+ function Get-EdgeTaskNames {
+ try {
+ $rows = @(schtasks.exe /Query /FO CSV /V 2>$null | ConvertFrom-Csv)
+ if (-not $rows -or $rows.Count -eq 0) { return @() }
+
+ $matches = $rows | Where-Object {
+ $tn = $_.TaskName
+ $a1 = $_.'Task To Run'
+ $a2 = $_.Actions
+ $a3 = $_.'Task Run'
+
+ ($NamePatterns | Where-Object { $tn -match $_ }).Count -gt 0 -or
+ (($a1 -and (($ActionPatterns | Where-Object { $a1 -match $_ }).Count -gt 0))) -or
+ (($a2 -and (($ActionPatterns | Where-Object { $a2 -match $_ }).Count -gt 0))) -or
+ (($a3 -and (($ActionPatterns | Where-Object { $a3 -match $_ }).Count -gt 0)))
+ }
+
+ return @($matches | Select-Object -ExpandProperty TaskName -Unique)
+ }
+ catch {
+ Write-Log -message ("EdgeTasks :: enumerate failed: {0}" -f $_.Exception.Message) -severity 'WARN'
+ return @()
+ }
+ }
+
+ function Test-TaskExists([string]$TaskName) {
+ try {
+ schtasks.exe /Query /TN "$TaskName" 1>$null 2>$null
+ return ($LASTEXITCODE -eq 0)
+ } catch {
+ return $true
+ }
+ }
+
+ function Remove-TaskWithRetries {
+ param(
+ [Parameter(Mandatory)][string]$TaskName
+ )
+
+ $deadline = (Get-Date).AddSeconds($PerTaskDeleteTimeoutSeconds)
+ $attempt = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $attempt++
+
+ try {
+ schtasks.exe /Delete /TN "$TaskName" /F 2>$null | Out-Null
+ $exit = $LASTEXITCODE
+
+ if ($exit -eq 0) {
+ if (-not (Test-TaskExists -TaskName $TaskName)) {
+ Write-Log -message ("EdgeTasks :: deleted {0} (attempt {1})" -f $TaskName, $attempt) -severity 'INFO'
+ return $true
+ }
+ Write-Log -message ("EdgeTasks :: delete reported success but task still exists: {0} (attempt {1})" -f $TaskName, $attempt) -severity 'WARN'
+ } else {
+ Write-Log -message ("EdgeTasks :: delete failed {0} (exit {1}, attempt {2})" -f $TaskName, $exit, $attempt) -severity 'WARN'
+ }
+ }
+ catch {
+ Write-Log -message ("EdgeTasks :: exception deleting {0} (attempt {1}): {2}" -f $TaskName, $attempt, $_.Exception.Message) -severity 'WARN'
+ }
+
+ Start-Sleep -Seconds $PerTaskRetryIntervalSeconds
+ }
+
+ Write-Log -message ("EdgeTasks :: timeout deleting {0} after {1}s" -f $TaskName, $PerTaskDeleteTimeoutSeconds) -severity 'ERROR'
+ return $false
+ }
+
+ Write-Log -message ("EdgeTasks :: begin (timeout={0}s, interval={1}s, perTaskTimeout={2}s)" -f $TimeoutSeconds, $RetryIntervalSeconds, $PerTaskDeleteTimeoutSeconds) -severity 'DEBUG'
+
+ $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
+ $pass = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $pass++
+ $targets = Get-EdgeTaskNames
+
+ if (-not $targets -or $targets.Count -eq 0) {
+ Write-Log -message ("EdgeTasks :: none found (pass {0})" -f $pass) -severity 'INFO'
+ Write-Log -message "EdgeTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ Write-Log -message ("EdgeTasks :: pass {0}: found {1} task(s)" -f $pass, $targets.Count) -severity 'INFO'
+
+ foreach ($tn in $targets) {
+ $null = Remove-TaskWithRetries -TaskName $tn
+ }
+
+ $stillThere = Get-EdgeTaskNames
+ if (-not $stillThere -or $stillThere.Count -eq 0) {
+ Write-Log -message ("EdgeTasks :: verification OK after pass {0}" -f $pass) -severity 'INFO'
+ Write-Log -message "EdgeTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ $remaining = [math]::Max(0, [int]($deadline - (Get-Date)).TotalSeconds)
+ Write-Log -message ("EdgeTasks :: still present after pass {0} (remaining {1}s). Sleeping {2}s..." -f $pass, $remaining, $RetryIntervalSeconds) -severity 'WARN'
+ Start-Sleep -Seconds $RetryIntervalSeconds
+ }
+
+ $final = Get-EdgeTaskNames
+ if ($final -and $final.Count -gt 0) {
+ $sample = ($final | Select-Object -First 10) -join '; '
+ Write-Log -message ("EdgeTasks :: timeout after {0}s. Remaining task(s): {1}" -f $TimeoutSeconds, $sample) -severity 'ERROR'
+ } else {
+ Write-Log -message "EdgeTasks :: end (success at timeout boundary)" -severity 'INFO'
+ }
+}
+
+function Disable-SyncFromCloud {
+ [CmdletBinding()]
+ param()
+
+ Write-Log -message "Disable-SyncFromCloud :: begin (disable Language settings sync)" -severity 'INFO'
+
+ # 1) Always do per-user disable (no admin required)
+ try {
+ $kUser = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\SettingSync\Groups\Language'
+ New-Item -Path $kUser -Force | Out-Null
+ New-ItemProperty -Path $kUser -Name 'Enabled' -PropertyType DWord -Value 0 -Force | Out-Null
+
+ $val = (Get-ItemProperty -Path $kUser -Name Enabled -ErrorAction SilentlyContinue).Enabled
+ Write-Log -message ("Disable-SyncFromCloud :: HKCU Language sync disabled (Enabled={0})" -f $val) -severity 'INFO'
+ }
+ catch {
+ Write-Log -message ("Disable-SyncFromCloud :: HKCU write failed: {0}" -f $_.Exception.Message) -severity 'WARN'
+ }
+
+ # 2) If elevated, also enforce via machine policy (optional hard block)
+ try {
+ $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
+ ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+
+ if ($isAdmin) {
+ $kPol = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\SettingSync'
+ New-Item -Path $kPol -Force | Out-Null
+
+ # Common policy convention: 2 = disable
+ New-ItemProperty -Path $kPol -Name 'DisableLanguageSettingSync' -PropertyType DWord -Value 2 -Force | Out-Null
+
+ $pval = (Get-ItemProperty -Path $kPol -Name DisableLanguageSettingSync -ErrorAction SilentlyContinue).DisableLanguageSettingSync
+ Write-Log -message ("Disable-SyncFromCloud :: HKLM policy set DisableLanguageSettingSync={0}" -f $pval) -severity 'INFO'
+ }
+ else {
+ Write-Log -message "Disable-SyncFromCloud :: not elevated; skipping HKLM policy enforcement" -severity 'DEBUG'
+ }
+ }
+ catch {
+ Write-Log -message ("Disable-SyncFromCloud :: HKLM policy step failed: {0}" -f $_.Exception.Message) -severity 'DEBUG'
+ }
+
+ Write-Log -message "Disable-SyncFromCloud :: complete (recommend sign out/in or reboot)" -severity 'INFO'
+}
+
+function Disable-SmartScreenStoreApps {
+ [CmdletBinding()]
+ param()
+
+ Write-Log -message "Disable-SmartScreenStoreApps :: begin (disable SmartScreen for Microsoft Store apps)" -severity 'INFO'
+
+ # Helper: normalize raw registry root strings to a PowerShell registry provider path
+ function Convert-ToRegistryProviderPath {
+ param([Parameter(Mandatory)][string]$Path)
+
+ switch -Regex ($Path) {
+ '^HKLM:\\' { return $Path }
+ '^HKCU:\\' { return $Path }
+ '^HKEY_LOCAL_MACHINE\\' { return "Registry::$Path" }
+ '^HKEY_CURRENT_USER\\' { return "Registry::$Path" }
+ default { return $Path }
+ }
+ }
+
+ # 1) Always do per-user disable (no admin required)
+ try {
+ $kUser = Convert-ToRegistryProviderPath 'HKCU:\Software\Microsoft\Windows\CurrentVersion\AppHost'
+ New-Item -Path $kUser -Force | Out-Null
+
+ # Disable SmartScreen for Microsoft Store apps
+ New-ItemProperty -Path $kUser -Name 'EnableWebContentEvaluation' -PropertyType DWord -Value 0 -Force | Out-Null
+
+ # Optional: allow override in UI (PreventOverride=0 means user can change it)
+ New-ItemProperty -Path $kUser -Name 'PreventOverride' -PropertyType DWord -Value 0 -Force | Out-Null
+
+ $valEnable = (Get-ItemProperty -Path $kUser -Name EnableWebContentEvaluation -ErrorAction SilentlyContinue).EnableWebContentEvaluation
+ $valOverride = (Get-ItemProperty -Path $kUser -Name PreventOverride -ErrorAction SilentlyContinue).PreventOverride
+
+ Write-Log -message ("Disable-SmartScreenStoreApps :: HKCU Store app SmartScreen disabled (EnableWebContentEvaluation={0}, PreventOverride={1})" -f $valEnable, $valOverride) -severity 'INFO'
+ }
+ catch {
+ Write-Log -message ("Disable-SmartScreenStoreApps :: HKCU step failed (path='{0}'): {1}" -f $kUser, $_.Exception.Message) -severity 'WARN'
+ }
+
+ # 2) If elevated, also set machine-wide (optional; affects all users)
+ try {
+ $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
+ ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+
+ if ($isAdmin) {
+
+ # Ensure HKLM: drive exists (rare edge-case; makes the function more robust)
+ if (-not (Get-PSDrive -Name HKLM -ErrorAction SilentlyContinue)) {
+ New-PSDrive -Name HKLM -PSProvider Registry -Root HKEY_LOCAL_MACHINE | Out-Null
+ }
+
+ $kMachine = Convert-ToRegistryProviderPath 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost'
+
+ # Debug breadcrumbs in case something upstream mutates the path
+ Write-Log -message ("Disable-SmartScreenStoreApps :: DEBUG kMachine='{0}'" -f $kMachine) -severity 'DEBUG'
+
+ New-Item -Path $kMachine -Force | Out-Null
+ New-ItemProperty -Path $kMachine -Name 'EnableWebContentEvaluation' -PropertyType DWord -Value 0 -Force | Out-Null
+
+ $mValEnable = (Get-ItemProperty -Path $kMachine -Name EnableWebContentEvaluation -ErrorAction SilentlyContinue).EnableWebContentEvaluation
+ Write-Log -message ("Disable-SmartScreenStoreApps :: HKLM Store app SmartScreen disabled (EnableWebContentEvaluation={0})" -f $mValEnable) -severity 'INFO'
+ }
+ else {
+ Write-Log -message "Disable-SmartScreenStoreApps :: not elevated; skipping HKLM machine-wide setting" -severity 'DEBUG'
+ }
+ }
+ catch {
+ Write-Log -message ("Disable-SmartScreenStoreApps :: HKLM step failed (path='{0}'): {1}" -f $kMachine, $_.Exception.Message) -severity 'DEBUG'
+ }
+
+ Write-Log -message "Disable-SmartScreenStoreApps :: complete (recommend sign out/in or restart Store apps)" -severity 'INFO'
+}
+
+
# Windows release ID.
# From time to time we need to have the different releases of the same OS version
$release_key = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion')
@@ -70,6 +563,23 @@ else {
$os_version = $null
}
+$worker_location = $null
+try {
+ $image_provisioner = Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet' -Name 'image_provisioner' -ErrorAction Stop
+} catch {
+ Write-Log -message ("Unable to read HKLM:\SOFTWARE\Mozilla\ronin_puppet\image_provisioner: {0}" -f $_.Exception.Message) -severity 'ERROR'
+ exit 1
+}
+
+if ($image_provisioner -match '(?i)mdc1') {
+ $worker_location = 'MDC1 hardware'
+} elseif ($image_provisioner -match '(?i)azure') {
+ $worker_location = 'azure vm'
+} else {
+ Write-Log -message ("Location can't be determined (image_provisioner='{0}')" -f $image_provisioner) -severity 'ERROR'
+ exit 1
+}
+
## Wait until explorer is set in the registry and then suppress notifications for firewall
while ($true) {
$explorer = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer" -ErrorAction SilentlyContinue
@@ -95,6 +605,15 @@ switch ($os_version) {
"win_11_2009" {
Write-Log -Message ('{0} :: {1} - {2:o}' -f $($MyInvocation.MyCommand.Name), "Setting scrollbars to always show in task-user-init.ps1", (Get-Date).ToUniversalTime()) -severity 'DEBUG'
New-ItemProperty -Path 'HKCU:\Control Panel\Accessibility' -Name 'DynamicScrollbars' -Value 0 -Force
+ if ($worker_location -eq 'MDC1 hardware') {
+ Disable-PerUserUwpServices
+ Remove-OneDriveScheduledTasks
+ Disable-OneDriveBackupPopup
+ Remove-EdgeScheduledTasks
+ ## Not currently functioning
+ #Disable-SyncFromCloud
+ #Disable-SmartScreenStoreApps
+ }
}
"win_2022" {
## Disable Server Manager Dashboard
diff --git a/modules/win_scheduled_tasks/files/gw_exe_check.ps1 b/modules/win_scheduled_tasks/files/gw_exe_check.ps1
index 6f3b97cb4..c687f33d1 100644
--- a/modules/win_scheduled_tasks/files/gw_exe_check.ps1
+++ b/modules/win_scheduled_tasks/files/gw_exe_check.ps1
@@ -29,9 +29,7 @@ function Set-PXE {
process {
$temp_dir = "$env:SystemDrive\temp\"
New-Item -ItemType Directory -Force -Path $temp_dir -ErrorAction SilentlyContinue
-
bcdedit /enum firmware > "$temp_dir\firmware.txt"
-
$fwbootmgr = Select-String -Path "$temp_dir\firmware.txt" -Pattern "{fwbootmgr}"
if (!$fwbootmgr) {
Write-Log -message ('{0} :: Device is configured for Legacy Boot. Exiting!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
@@ -41,7 +39,6 @@ function Set-PXE {
$FullLine = ((Get-Content "$temp_dir\firmware.txt" | Select-String "IPV4|EFI Network" -Context 1 -ErrorAction Stop).Context.PreContext)[0]
$GUID = '{' + $FullLine.Split('{')[1]
bcdedit /set "{fwbootmgr}" bootsequence "$GUID"
-
Write-Log -message ('{0} :: Device will PXE boot. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
Restart-Computer -Force
Exit
@@ -56,62 +53,73 @@ function Set-PXE {
}
}
-# Main monitoring script
-
-# Initial sleep for 10 minutes
-Write-Log -message "Sleeping 10 minutes before starting GW monitoring..." -severity 'DEBUG'
-Start-Sleep -Seconds (10 * 60)
-
-$regPath = "HKLM:\SOFTWARE\Mozilla\Ronin"
-$regName = "GW_failed"
-
-while ($true) {
- Write-Log -message "Checking for 'generic-worker' process..." -severity 'DEBUG'
-
- $process = Get-Process -Name "generic-worker" -ErrorAction SilentlyContinue
+function Register-FailureAndMaybePXE {
+ param (
+ [string] $regName
+ )
- if (-not $process) {
- Write-Log -message "Generic Worker process not found." -severity 'WARN'
+ $regPath = "HKLM:\SOFTWARE\Mozilla\Ronin\GW_check_failures"
+ if (!(Test-Path $regPath)) {
+ New-Item -Path $regPath -Force | Out-Null
+ }
+ $currentValue = 0
+ try {
+ $currentValue = (Get-ItemProperty -Path $regPath -Name $regName -ErrorAction Stop).$regName
+ } catch {
+ $currentValue = 0
+ }
+ if ($currentValue -eq 1) {
+ Write-Log -message "$regName failure occurred again. Initiating PXE boot." -severity 'ERROR'
+ Set-PXE
+ exit
+ } else {
+ Write-Log -message "$regName failure detected. Rebooting system (1st failure)." -severity 'ERROR'
+ Set-ItemProperty -Path $regPath -Name $regName -Value 1 -Force
+ Restart-Computer -Force
+ Exit
+ }
+}
- if (!(Test-Path $regPath)) {
- New-Item -Path $regPath -Force | Out-Null
- }
+$bootstrap_stage = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").bootstrap_stage
+If ($bootstrap_stage -ne 'complete') {
+ Write-Log -message ('{0} :: Bootstrap has not completed. EXITING!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Exit
+}
- $currentValue = $null
- Try {
- $currentValue = (Get-ItemProperty -Path $regPath -Name $regName -ErrorAction Stop).$regName
- } Catch {
- # If the property does not exist, we'll treat it as first failure
- $currentValue = $null
- }
+# Uptime check — allow 15-minute grace period before enforcing logic
+$lastBoot = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
+$uptimeMinutes = (New-TimeSpan -Start $lastBoot -End (Get-Date)).TotalMinutes
+if ($uptimeMinutes -lt 15) {
+ Write-Log -message "System has only been up for $([math]::Round($uptimeMinutes, 1)) minutes. Skipping generic-worker check until 15-minute threshold is met." -severity 'DEBUG'
+ exit
+}
- if ($currentValue -eq 1) {
- Write-Log -message "Generic Worker still missing after previous failure. Initiating PXE boot..." -severity 'ERROR'
- Set-PXE
- # Set-PXE will reboot and exit
- } else {
- Write-Log -message "First failure detected. Setting GW_failed=1." -severity 'WARN'
- Set-ItemProperty -Path $regPath -Name $regName -Value 1 -Force
- }
- } else {
- Write-Log -message "Generic Worker process is running." -severity 'DEBUG'
+# Var set by the maintain system script
+# Check gw_initiated env var
+if ($env:gw_initiated -ne 'true') {
+ Write-Log -message "Environment variable gw_initiated is not true." -severity 'WARN'
+ Register-FailureAndMaybePXE -regName 'gw_initiated_failed'
+}
- if (Test-Path $regPath) {
- $currentValue = $null
- Try {
- $currentValue = (Get-ItemProperty -Path $regPath -Name $regName -ErrorAction Stop).$regName
- } Catch {
- $currentValue = $null
- }
+# Check for generic-worker process
+# Write-Log -message "Checking for 'generic-worker' process..." -severity 'DEBUG'
+$process = Get-Process -Name "generic-worker" -ErrorAction SilentlyContinue
+if (-not $process) {
+ Write-Log -message "generic-worker has not started." -severity 'WARN'
+ Register-FailureAndMaybePXE -regName 'process_failed'
+}
- if ($currentValue -eq 1) {
- Write-Log -message "Generic Worker recovered. Removing GW_failed flag." -severity 'DEBUG'
- Remove-ItemProperty -Path $regPath -Name $regName -Force
+# Success path – clear failure flags
+Write-Log -message "Generic-worker process is up and running." -severity 'DEBUG'
+$regPath = "HKLM:\SOFTWARE\Mozilla\Ronin\GW_check_failures"
+$failKeys = @('gw_initiated_failed', 'process_failed')
+foreach ($key in $failKeys) {
+ if (Test-Path $regPath) {
+ try {
+ if ((Get-ItemProperty -Path $regPath -Name $key -ErrorAction Stop).$key -eq 1) {
+ Write-Log -message "Clearing $key failure flag." -severity 'DEBUG'
+ Remove-ItemProperty -Path $regPath -Name $key -Force
}
- }
+ } catch {}
}
-
- # Sleep 5 minutes before next check
- Write-Log -message "Sleeping 5 minutes until next check..." -severity 'DEBUG'
- Start-Sleep -Seconds (5 * 60)
}
diff --git a/modules/win_scheduled_tasks/files/maintainsystem-hw.ps1 b/modules/win_scheduled_tasks/files/maintainsystem-hw.ps1
index db5803445..addc8941f 100644
--- a/modules/win_scheduled_tasks/files/maintainsystem-hw.ps1
+++ b/modules/win_scheduled_tasks/files/maintainsystem-hw.ps1
@@ -55,234 +55,56 @@ function Run-MaintainSystem {
Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
}
}
-function Remove-OldTaskDirectories {
- param (
- [string[]] $targets = @('Z:\task_*', 'C:\Users\task_*')
- )
- begin {
- Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
- process {
- foreach ($target in ($targets | Where-Object { (Test-Path -Path ('{0}:\' -f $_[0]) -ErrorAction SilentlyContinue) })) {
- $all_task_paths = @(Get-ChildItem -Path $target | Sort-Object -Property { $_.LastWriteTime })
- if ($all_task_paths.length -gt 1) {
- Write-Log -message ('{0} :: {1} task directories detected matching pattern: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths.length, $target) -severity 'INFO'
- $old_task_paths = $all_task_paths[0..($all_task_paths.Length - 2)]
- foreach ($old_task_path in $old_task_paths) {
- try {
- & takeown.exe @('/a', '/f', $old_task_path, '/r', '/d', 'Y')
- & icacls.exe @($old_task_path, '/grant', 'Administrators:F', '/t')
- Remove-Item -Path $old_task_path -Force -Recurse
- Write-Log -message ('{0} :: removed task directory: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime) -severity 'INFO'
- }
- catch {
- Write-Log -message ('{0} :: failed to remove task directory: {1}, with last write time: {2}. {3}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime, $_.Exception.Message) -severity 'ERROR'
- }
- }
- }
- elseif ($all_task_paths.length -eq 1) {
- Write-Log -message ('{0} :: a single task directory was detected at: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths[0].FullName, $all_task_paths[0].LastWriteTime) -severity 'DEBUG'
- }
- else {
- Write-Log -message ('{0} :: no task directories detected matching pattern: {1}' -f $($MyInvocation.MyCommand.Name), $target) -severity 'DEBUG'
- }
- }
- }
- end {
- Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
-}
-function Check-RoninNodeOptions {
- param (
- [string] $inmutable = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").inmutable,
- [string] $flagfile = "$env:programdata\PuppetLabs\ronin\semaphore\task-claim-state.valid"
- )
- begin {
- Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
- process {
- Write-Host $inmutable
- if ($inmutable -eq 'true') {
- Write-Log -message ('{0} :: Node is set to be inmutable' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Remove-Item -path $lock -ErrorAction SilentlyContinue
- write-host New-item -path $flagfile
- Exit-PSSession
- }
- }
- end {
- Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
-}
-
-Function UpdateRonin {
- param (
- [string] $sourceOrg,
- [string] $sourceRepo,
- [string] $sourceBranch,
- [string] $ronin_repo = "$env:systemdrive\ronin"
- )
- begin {
- Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
- process {
- $sourceOrg = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Organisation' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Organisation').Organisation } else { 'mozilla-platform-ops' })
- $sourceRepo = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Repository' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Repository').Repository } else { 'ronin_puppet' })
- $sourceBranch = $(if ((Test-Path -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Branch' -ErrorAction SilentlyContinue)) { (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Mozilla\ronin_puppet\Source' -Name 'Branch').Branch } else { 'master' })
-
- Set-Location $ronin_repo
- git config --global --add safe.directory "C:/ronin"
- git pull https://github.com/$sourceOrg/$sourceRepo $sourceBranch
- $git_exit = $LastExitCode
- if ($git_exit -eq 0) {
- $git_hash = (git rev-parse --verify HEAD)
- Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name githash -type string -value $git_hash
- Write-Log -message ('{0} :: Checking/pulling updates from https://github.com/{1}/{2}. Branch: {3}.' -f $($MyInvocation.MyCommand.Name), ($sourceOrg), ($sourceRepo), ($sourceRev)) -severity 'DEBUG'
- }
- else {
- # Fall back to clone if pull fails
- Write-Log -message ('{0} :: Git pull failed! https://github.com/{1}/{2}. Branch: {3}.' -f $($MyInvocation.MyCommand.Name), ($sourceOrg), ($sourceRepo), ($sourceRev)) -severity 'DEBUG'
- Write-Log -message ('{0} :: Deleting old repository and cloning repository .' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Move-item -Path $ronin_repo\manifests\nodes.pp -Destination $env:TEMP\nodes.pp
- Move-item -Path $ronin_repo\data\secrets\vault.yaml -Destination $env:TEMP\vault.yaml
- #Remove-Item -Recurse -Force $ronin_repo
- Start-Sleep -s 2
- git clone --single-branch --branch $sourceRev https://github.com/$sourceOrg/$sourceRepo $ronin_repo
- Move-item -Path $env:TEMP\nodes.pp -Destination $ronin_repo\manifests\nodes.pp
- Move-item -Path $env:TEMP\vault.yaml -Destination $ronin_repo\data\secrets\vault.yaml
- }
- }
- end {
- Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
-}
-function Puppet-Run {
- param (
- [int] $exit,
- [string] $lock = "$env:programdata\PuppetLabs\ronin\semaphore\ronin_run.lock",
- [int] $last_exit = (Get-ItemProperty "HKLM:\SOFTWARE\Mozilla\ronin_puppet").last_run_exit,
- [string] $run_to_success = (Get-ItemProperty "HKLM:\SOFTWARE\Mozilla\ronin_puppet").runtosuccess,
- [string] $nodes_def = "$env:systemdrive\ronin\manifests\nodes\nodes.pp",
- [string] $logdir = "$env:systemdrive\logs",
- [string] $fail_dir = "$env:systemdrive\fail_logs",
- [string] $log_file = "$datetime-puppetrun.log",
- [string] $datetime = (get-date -format yyyyMMdd-HHmm),
- [string] $flagfile = "$env:programdata\PuppetLabs\ronin\semaphore\task-claim-state.valid"
+function Invoke-DownloadWithRetryGithub {
+ Param(
+ [Parameter(Mandatory)] [string] $Url,
+ [Alias("Destination")] [string] $Path,
+ [string] $PAT
)
- begin {
- Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
- process {
-
- Check-RoninNodeOptions
- UpdateRonin
-
- # Setting Env variabes for PuppetFile install and Puppet run
- # The ssl variables are needed for R10k
- Write-Log -message ('{0} :: Setting Puppet enviroment.' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- $env:path = "$env:programfiles\Puppet Labs\Puppet\puppet\bin;$env:programfiles\Puppet Labs\Puppet\bin;$env:path"
- $env:SSL_CERT_FILE = "$env:programfiles\Puppet Labs\Puppet\puppet\ssl\cert.pem"
- $env:SSL_CERT_DIR = "$env:programfiles\Puppet Labs\Puppet\puppet\ssl"
- $env:FACTER_env_windows_installdir = "$env:programfiles\Puppet Labs\Puppet"
- $env:HOMEPATH = "\Users\Administrator"
- $env:HOMEDRIVE = "C:"
- $env:PL_BASEDIR = "$env:programfiles\Puppet Labs\Puppet"
- $env:PUPPET_DIR = "$env:programfiles\Puppet Labs\Puppet"
- $env:RUBYLIB = "$env:programfiles\Puppet Labs\Puppet\lib"
- $env:USERNAME = "Administrator"
- $env:USERPROFILE = "$env:systemdrive\Users\Administrator"
-
- # This is temporary and should be removed after the cloud_windows branch is merged
- # Hiera lookups will fail after the merge if this is not in place following the merge
- <#
- if((test-path $env:systemdrive\ronin\win_hiera.yaml)) {
- $hiera = "win_hiera.yaml"
- } else {
- $hiera = "hiera.yaml"
- }
- #>
- # this will break Win 10 1803 if this is merged into the master brnach
- $hiera = "hiera.yaml"
-
- # Needs to be removed from path or a wrong puppet file will be used
- $env:path = ($env:path.Split(';') | Where-Object { $_ -ne "$env:programfiles\Puppet Labs\Puppet\puppet\bin" }) -join ';'
- If (!(test-path $fail_dir)) {
- New-Item -ItemType Directory -Force -Path $fail_dir
- }
- Get-ChildItem -Path $logdir\*.log -Recurse | Move-Item -Destination $logdir\old -ErrorAction SilentlyContinue
- Write-Log -message ('{0} :: Initiating Puppet apply .' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- puppet apply manifests\nodes.pp --onetime --verbose --no-daemonize --no-usecacheonfailure --detailed-exitcodes --no-splay --show_diff --modulepath=modules`;r10k_modules --hiera_config=$hiera --logdest $logdir\$log_file
- [int]$puppet_exit = $LastExitCode
-
- if ($run_to_success -eq 'true') {
- if (($puppet_exit -ne 0) -and ($puppet_exit -ne 2)) {
- if ($last_exit -eq 0) {
- Write-Log -message ('{0} :: Puppet apply failed.' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
- Remove-Item $lock -ErrorAction SilentlyContinue
- # If the Puppet run fails send logs to papertrail
- # Nxlog watches $fail_dir for files names *-puppetrun.log
- Move-Item $logdir\$log_file -Destination $fail_dir
- shutdown @('-r', '-t', '0', '-c', 'Reboot; Puppet apply failed', '-f', '-d', '4:5')
- }
- elseif ($last_exit -ne 0) {
- Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
- Remove-Item $lock
- Move-Item $logdir\$log_file -Destination $fail_dir
- Write-Log -message ('{0} :: Puppet apply failed. Waiting 10 minutes beofre Reboot' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Start-Sleep 600
- shutdown @('-r', '-t', '0', '-c', 'Reboot; Puppet apply failed', '-f', '-d', '4:5')
- }
- }
- elseif (($puppet_exit -match 0) -or ($puppet_exit -match 2)) {
- Write-Log -message ('{0} :: Puppet apply successful' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $puppet_exit
- Remove-Item -path $lock
- New-item -path $flagfile
+ if (-not $Path) {
+ $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
+ $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
+ $fileName = [IO.Path]::GetFileName($Url) -replace $re
+ if ([String]::IsNullOrEmpty($fileName)) { $fileName = [System.IO.Path]::GetRandomFileName() }
+ $Path = Join-Path -Path "${env:Temp}" -ChildPath $fileName
+ }
+ Write-Host "Downloading package from $Url to $Path..."
+ $interval = 30
+ $downloadStartTime = Get-Date
+ for ($retries = 20; $retries -gt 0; $retries--) {
+ try {
+ $attemptStartTime = Get-Date
+ $Headers = @{
+ Accept = "application/vnd.github+json"
+ Authorization = "Bearer $($PAT)"
+ "X-GitHub-Api-Version" = "2022-11-28"
}
- else {
- Write-Log -message ('{0} :: Unable to detrimine state post Puppet apply' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Set-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet -name last_exit -type dword -value $last_exit
- Move-Item $logdir\$log_file -Destination $fail_dir
- Remove-Item -path $lock
- shutdown @('-r', '-t', '600', '-c', 'Reboot; Unveriable state', '-f', '-d', '4:5')
+ $response = Invoke-WebRequest -Uri $Url -Headers $Headers -OutFile $Path
+ $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)
+ Write-Host "Package downloaded in $attemptSeconds seconds"
+ Write-Host "Status: $($response.statuscode)"
+ break
+ } catch {
+ $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)
+ Write-Warning "Package download failed in $attemptSeconds seconds"
+ Write-Host "Status: $($response.statuscode)"
+ Write-Warning $_.Exception.Message
+ if ($_.Exception.InnerException.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
+ Write-Warning "Request returned 404 Not Found. Aborting download."
+ $retries = 0
}
}
- }
- end {
- Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
-}
-
-function StartWorkerRunner {
- param (
- )
- begin {
- Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
- }
- process {
- ## Checking for issues with the user profile.
- $lastBootTime = Get-WinEvent -LogName "System" -FilterXPath "" |
- Select-Object -First 1 |
- ForEach-Object { $_.TimeCreated }
- $eventIDs = @(1511, 1515)
-
- $events = Get-WinEvent -LogName "Application" |
- Where-Object { $_.ID -in $eventIDs -and $_.TimeCreated -gt $lastBootTime } |
- Sort-Object TimeCreated -Descending | Select-Object -First 1
-
- if ($events) {
- Write-Log -message ('{0} :: Possible User Profile Corruption. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Restart-Computer -Force
- exit
+ if ($retries -eq 0) {
+ $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)
+ throw "Package download failed after $totalSeconds seconds"
}
- Start-Service -Name worker-runner
- }
- end {
- Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ Write-Warning "Waiting $interval seconds before retrying (retries left: $retries)..."
+ Start-Sleep -Seconds $interval
}
+ return $Path
}
-function CompareConfig {
+function CompareConfigBasic {
param (
[string]$yaml_url = "https://raw.githubusercontent.com/mozilla-platform-ops/worker-images/refs/heads/main/provisioners/windows/MDC1Windows/pools.yml",
[string]$PAT
@@ -294,227 +116,240 @@ function CompareConfig {
process {
- $yaml = $null
-
$SETPXE = $false
-
+ $yaml = $null
+ $yamlHash = $null
$IPAddress = $null
- $Ethernet = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() | Where-Object { $_.Name -match "ethernet" }
+
+ # === Retrieve IP address ===
+ $Ethernet = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() |
+ Where-Object { $_.Name -match "ethernet" }
try {
$IPAddress = ($Ethernet.GetIPProperties().UnicastAddresses |
Where-Object { $_.Address.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork -and $_.Address.IPAddressToString -ne "127.0.0.1" } |
Select-Object -First 1 -ExpandProperty Address).IPAddressToString
-
- if (-not $IPAddress) {
- throw "No IP address found using .NET method."
- }
}
catch {
try {
$NetshOutput = netsh interface ip show addresses
$IPAddress = ($NetshOutput -match "IP Address" | ForEach-Object {
- if ($_ -notmatch "127.0.0.1") {
- $_ -replace ".*?:\s*", ""
- }
+ if ($_ -notmatch "127.0.0.1") { $_ -replace ".*?:\s*", "" }
})[0]
}
catch {
- Write-Log -message "Failed to get IP address" -severity 'ERROR'
+ Write-Log -message ('{0} :: Failed to get IP address' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
}
}
- if ($IPAddress) {
- Write-Log -message "IP Address: $IPAddress" -severity 'INFO'
- }
- else {
- Write-Log -message "No IP Address could be determined." -severity 'ERROR'
+ if (-not $IPAddress) {
+ Write-Log -message ('{0} :: No IP Address could be determined.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Restart-Computer -Force
return
}
+ Write-Log -message ('{0} :: IP Address: {1}' -f $MyInvocation.MyCommand.Name, $IPAddress) -severity 'INFO'
+
+
+ # === Resolve DNS to get worker name ===
try {
$ResolvedName = (Resolve-DnsName -Name $IPAddress -Server "10.48.75.120").NameHost
}
catch {
- Write-Log -message "DNS resolution failed." -severity 'ERROR'
+ Write-Log -message ('{0} :: DNS resolution failed' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Restart-Computer -Force
return
}
- Write-Log -message "Resolved Name: $ResolvedName" -severity 'INFO'
+ Write-Log -message ('{0} :: Resolved Name: {1}' -f $MyInvocation.MyCommand.Name, $ResolvedName) -severity 'INFO'
$index = $ResolvedName.IndexOf('.')
if ($index -lt 0) {
- Write-Log -message "Invalid hostname format." -severity 'ERROR'
+ Write-Log -message ('{0} :: Invalid hostname format.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Restart-Computer -Force
return
}
$worker_node_name = $ResolvedName.Substring(0, $index)
- $domain_suffix = $ResolvedName.Substring($index + 1)
+ Write-Log -message ('{0} :: Host name set to: {1}' -f $MyInvocation.MyCommand.Name, $worker_node_name) -severity 'INFO'
- Write-Log -message "Host name set to: $worker_node_name" -severity 'INFO'
+ # === Load local ronin puppet values ===
$localHash = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).GITHASH
$localPool = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).worker_pool_id
- $maxRetries = 5
- $retryDelay = 10
- $attempt = 0
- $success = $false
- while ($attempt -lt $maxRetries -and -not $success) {
- try {
- $Headers = @{
- Accept = "application/vnd.github+json"
- Authorization = "Bearer $($PAT)"
- "X-GitHub-Api-Version" = "2022-11-28"
- }
- $response = Invoke-WebRequest -Uri $yaml_url -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop -Headers $Headers
- $yaml = $response.Content | ConvertFrom-Yaml
- if ($yaml) {
- $success = $true
- }
- else {
- throw "YAML content is empty"
- Write-Log -message "YAML content is empty" -severity 'WARN'
- }
- }
- catch {
- Write-Log -message "Attempt $($attempt + 1): Failed to fetch YAML - $_" -severity 'WARN'
- Start-Sleep -Seconds $retryDelay
- $attempt++
- }
+ # === Load PAT for YAML download ===
+ $patFile = "D:\Secrets\pat.txt"
+ if (-not (Test-Path $patFile)) {
+ Write-Log -message ('{0} :: PAT file missing: {1}' -f $MyInvocation.MyCommand.Name, $patFile) -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
+ }
+
+ $PAT = Get-Content $patFile -ErrorAction Stop
+
+
+ # === Download YAML using unified retry function ===
+ $tempYamlPath = "$env:TEMP\pools.yml"
+
+ $splat = @{
+ Url = $yaml_url
+ Path = $tempYamlPath
+ PAT = $PAT
}
- if (-not $success) {
- Write-Log -message "YAML could not be loaded after $maxRetries attempts." -severity 'ERROR'
+ if (-not (Invoke-DownloadWithRetryGithub @splat)) {
+ Write-Log -message ('{0} :: YAML download failed after retries. PXE rebooting.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
return
}
+
+ # === Parse YAML ===
+ try {
+ $yaml = Get-Content $tempYamlPath -Raw | ConvertFrom-Yaml
+ }
+ catch {
+ Write-Log -message ('{0} :: YAML parsing failed: {1}' -f $MyInvocation.MyCommand.Name, $_) -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
+ }
+
+
+ # === Lookup this worker in pools.yml ===
$found = $false
foreach ($pool in $yaml.pools) {
- foreach ($node in $pool.nodes) {
- if ($node -eq $worker_node_name) {
- $WorkerPool = $pool.name
- $yamlHash = $pool.hash
- $yamlImageName = $pool.image
- $yamlImageDir = "D:\" + $yamlImageName
- $found = $true
- break
- }
+ if ($pool.nodes -contains $worker_node_name) {
+ $WorkerPool = $pool.name
+ $yamlHash = $pool.hash
+ $yamlImageName = $pool.image
+ $yamlImageDir = "D:\" + $yamlImageName
+ $found = $true
+ break
}
- if ($found) { break }
}
if (-not $found) {
- Write-Log -message "Node name not found in YAML!!" -severity 'ERROR'
- exit 96
+ Write-Log -message ('{0} :: Node not found in YAML. PXE rebooting.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Set-PXE
+ Restart-Computer -Force
+ return
}
- Write-Log -message "=== Configuration Comparison ===" -severity 'INFO'
+ Write-Log -message ('{0} :: === Configuration Comparison ===' -f $MyInvocation.MyCommand.Name) -severity 'INFO'
- if ($localPool -eq $WorkerPool) {
- Write-Log -message "Worker Pool Match: $WorkerPool" -severity 'INFO'
+
+ # === Compare pool ===
+ if ($localPool -ne $WorkerPool) {
+ Write-Log -message ('{0} :: Worker Pool MISMATCH!' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ $SETPXE = $true
}
else {
- Write-Log -message "Worker Pool MISMATCH!" -severity 'ERROR'
- $SETPXE = $true
- Start-Sleep -s 1
+ Write-Log -message ('{0} :: Worker Pool Match: {1}' -f $MyInvocation.MyCommand.Name, $WorkerPool) -severity 'INFO'
}
- if ($localHash -eq $yamlHash) {
- Write-Log -message "Git Hash Match: $yamlHash" -severity 'INFO'
+
+ # === Compare puppet githash ===
+ if ([string]::IsNullOrWhiteSpace($yamlHash) -or $localHash -ne $yamlHash) {
+ Write-Log -message ('{0} :: Git Hash MISMATCH or missing YAML hash!' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Write-Log -message ('{0} :: Local: {1}' -f $MyInvocation.MyCommand.Name, $localHash) -severity 'WARN'
+ Write-Log -message ('{0} :: YAML : {1}' -f $MyInvocation.MyCommand.Name, $yamlHash) -severity 'WARN'
+ $SETPXE = $true
}
else {
- Write-Log -message "Git Hash MISMATCH!" -severity 'ERROR'
- Write-Log -message "Local: $localHash" -severity 'WARN'
- Write-Log -message "YAML : $yamlHash" -severity 'WARN'
- $SETPXE = $true
- Start-Sleep -s 1
+ Write-Log -message ('{0} :: Git Hash Match: {1}' -f $MyInvocation.MyCommand.Name, $yamlHash) -severity 'INFO'
}
+ # === Verify local puppet image directory exists ===
if (!(Test-Path $yamlImageDir)) {
- Write-Log -message "Image Directory MISMATCH!" -severity 'ERROR'
- Write-Log -message "YAML : $yamlImageDir NOT FOUND" -severity 'WARN'
+ Write-Log -message ('{0} :: Image directory missing: {1}' -f $MyInvocation.MyCommand.Name, $yamlImageDir) -severity 'ERROR'
$SETPXE = $true
- Start-Sleep -s 1
}
+
+
+ # === If anything mismatched, PXE reboot ===
if ($SETPXE) {
- Write-Log -message "Configuration MISMATCH! Initiating self re-deploy!" -severity 'ERROR'
+ Write-Log -message ('{0} :: Configuration mismatch — initiating PXE + reboot.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
Set-PXE
+ Restart-Computer -Force
+ return
}
- Write-Log -message "SETPXE set to: $SETPXE" -severity 'DEBUG'
- }
+ Write-Log -message ('{0} :: Configuration is correct. No reboot required.' -f $MyInvocation.MyCommand.Name) -severity 'INFO'
+ }
end {
Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
}
}
+function Remove-OldTaskDirectories {
+ param (
+ [string[]] $targets = @('Z:\task_*', 'C:\Users\task_*')
+ )
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+ process {
+ foreach ($target in ($targets | Where-Object { (Test-Path -Path ('{0}:\' -f $_[0]) -ErrorAction SilentlyContinue) })) {
+ $all_task_paths = @(Get-ChildItem -Path $target | Sort-Object -Property { $_.LastWriteTime })
+ if ($all_task_paths.length -gt 1) {
+ Write-Log -message ('{0} :: {1} task directories detected matching pattern: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths.length, $target) -severity 'INFO'
+ $old_task_paths = $all_task_paths[0..($all_task_paths.Length - 2)]
+ foreach ($old_task_path in $old_task_paths) {
+ try {
+ & takeown.exe @('/a', '/f', $old_task_path, '/r', '/d', 'Y')
+ & icacls.exe @($old_task_path, '/grant', 'Administrators:F', '/t')
+ Remove-Item -Path $old_task_path -Force -Recurse
+ Write-Log -message ('{0} :: removed task directory: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime) -severity 'INFO'
+ }
+ catch {
+ Write-Log -message ('{0} :: failed to remove task directory: {1}, with last write time: {2}. {3}' -f $($MyInvocation.MyCommand.Name), $old_task_path.FullName, $old_task_path.LastWriteTime, $_.Exception.Message) -severity 'ERROR'
+ }
+ }
+ }
+ elseif ($all_task_paths.length -eq 1) {
+ Write-Log -message ('{0} :: a single task directory was detected at: {1}, with last write time: {2}' -f $($MyInvocation.MyCommand.Name), $all_task_paths[0].FullName, $all_task_paths[0].LastWriteTime) -severity 'DEBUG'
+ }
+ else {
+ Write-Log -message ('{0} :: no task directories detected matching pattern: {1}' -f $($MyInvocation.MyCommand.Name), $target) -severity 'DEBUG'
+ }
+ }
+ }
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
-function StartGenericWorker {
+function StartWorkerRunner {
param (
- [string] $GW_dir = "$env:systemdrive\generic-worker"
)
begin {
Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
}
process {
- # Check for user profile issues
+ ## Checking for issues with the user profile.
$lastBootTime = Get-WinEvent -LogName "System" -FilterXPath "" |
Select-Object -First 1 |
ForEach-Object { $_.TimeCreated }
-
$eventIDs = @(1511, 1515)
+
$events = Get-WinEvent -LogName "Application" |
Where-Object { $_.ID -in $eventIDs -and $_.TimeCreated -gt $lastBootTime } |
Sort-Object TimeCreated -Descending | Select-Object -First 1
if ($events) {
Write-Log -message ('{0} :: Possible User Profile Corruption. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- Start-Sleep -Seconds 5
Restart-Computer -Force
exit
}
-
- Set-Location -Path $GW_dir
-
- & $GW_dir\generic-worker.exe run --config generic-worker.config 2>&1 | Out-File -FilePath generic-worker.log -Encoding utf8
- $exitCode = $LASTEXITCODE
-
- Write-Log -message ('{0} :: GW exited with code {1}' -f $($MyInvocation.MyCommand.Name), $exitCode) -severity 'DEBUG'
-
- switch ($exitCode) {
- 68 {
-
- Write-Log -message ('{0} :: Idle timeout detected (exit code 68). Checking for latest Config.' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- CompareConfig -PAT (Get-Content "D:\Secrets\pat.txt")
-
- Write-Log -message ('{0} :: Copying current-task-user.json to next-task-user.json' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- $src = Join-Path $GW_dir 'current-task-user.json'
- $dest = Join-Path $GW_dir 'next-task-user.json'
- if (Test-Path $src) {
- Copy-Item -Path $src -Destination $dest -Force
- Write-Log -message ('{0} :: File copied successfully' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
- }
- else {
- Write-Log -message ('{0} :: Source file not found: {1}' -f $($MyInvocation.MyCommand.Name), $src) -severity 'WARNING'
- }
-
- ## Restart Explorer/close out exisiting windows
- Stop-Process -Name explorer -Force
-
- Start-Sleep -s 2
- StartGenericWorker
- return
- }
- default {
- Write-Log -message ('{0} :: Non-idle exit code {1}. Rebooting' -f $($MyInvocation.MyCommand.Name), $exitCode) -severity 'DEBUG'
- Start-Sleep -Seconds 1
- Restart-Computer -Force
- }
- }
+ Start-Service -Name worker-runner
+ [Environment]::SetEnvironmentVariable('gw_initiated', 'true', 'Machine')
}
end {
Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
@@ -606,7 +441,6 @@ function Set-PXE {
Write-Log -message ('{0} :: Device will PXE boot. Restarting' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
Restart-Computer -Force
- Exit
}
Catch {
Write-Log -message ('{0} :: Unable to set next boot to PXE. Exiting!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
@@ -656,16 +490,8 @@ if ($refresh_rate -ne "60") {
$bootstrap_stage = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").bootstrap_stage
If ($bootstrap_stage -eq 'complete') {
-
- $tasks = Get-ScheduledTask | Where-Object { $_.TaskName -eq "bootstrap" }
-
- if ($tasks) {
- $tasks | ForEach-Object {
- Stop-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath
- Unregister-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath -Confirm:$false
- Write-Host "Deleted task '$($_.TaskName)' at path '$($_.TaskPath)'."
- }
- }
+ CompareConfigBasic
+ Start-Sleep -Seconds 2
Run-MaintainSystem
## We're getting user profile corruption errors, so let's check that the user is logged in using quser.exe
for ($i = 0; $i -lt 3; $i++) {
@@ -679,7 +505,6 @@ If ($bootstrap_stage -eq 'complete') {
break
}
}
-
## Let's make sure the machine is online before checking the internet
Test-ConnectionUntilOnline
@@ -687,12 +512,8 @@ If ($bootstrap_stage -eq 'complete') {
## Instead of querying chocolatey each time this runs, let's query chrome json endoint and check locally installed version
Get-LatestGoogleChrome
- $processname = "StartMenuExperienceHost"
- if ($null -ne $process) {
- Stop-Process -Name $processname -force
- }
- CompareConfig -PAT (Get-Content "D:\Secrets\pat.txt")
- StartGenericWorker
+ StartWorkerRunner
+ Exit-PSSession
}
else {
Write-Log -message ('{0} :: Bootstrap has not completed. EXITING!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
diff --git a/modules/win_scheduled_tasks/files/onedrive_task_deletion.ps1 b/modules/win_scheduled_tasks/files/onedrive_task_deletion.ps1
new file mode 100644
index 000000000..2350f14e0
--- /dev/null
+++ b/modules/win_scheduled_tasks/files/onedrive_task_deletion.ps1
@@ -0,0 +1,167 @@
+<#
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#>
+
+function Write-Log {
+ param (
+ [string] $message,
+ [string] $severity = 'INFO',
+ [string] $source = 'MaintainSystem',
+ [string] $logName = 'Application'
+ )
+ if (!([Diagnostics.EventLog]::Exists($logName)) -or !([Diagnostics.EventLog]::SourceExists($source))) {
+ New-EventLog -LogName $logName -Source $source
+ }
+ switch ($severity) {
+ 'DEBUG' {
+ $entryType = 'SuccessAudit'
+ $eventId = 2
+ break
+ }
+ 'WARN' {
+ $entryType = 'Warning'
+ $eventId = 3
+ break
+ }
+ 'ERROR' {
+ $entryType = 'Error'
+ $eventId = 4
+ break
+ }
+ default {
+ $entryType = 'Information'
+ $eventId = 1
+ break
+ }
+ }
+ Write-EventLog -LogName $logName -Source $source -EntryType $entryType -Category 0 -EventID $eventId -Message $message
+ if ([Environment]::UserInteractive) {
+ $fc = @{ 'Information' = 'White'; 'Error' = 'Red'; 'Warning' = 'DarkYellow'; 'SuccessAudit' = 'DarkGray' }[$entryType]
+ Write-Host -object $message -ForegroundColor $fc
+ }
+}
+
+function Remove-OneDriveScheduledTasks {
+ [CmdletBinding()]
+ param(
+ [int]$TimeoutSeconds = 180,
+ [int]$RetryIntervalSeconds = 10,
+ [int]$PerTaskDeleteTimeoutSeconds = 60,
+ [int]$PerTaskRetryIntervalSeconds = 3
+ )
+
+ function Get-OneDriveTaskNames {
+ try {
+ $rows = @(schtasks.exe /Query /FO CSV /V 2>$null | ConvertFrom-Csv)
+ if (-not $rows -or $rows.Count -eq 0) { return @() }
+
+ $matches = $rows | Where-Object {
+ ($_.TaskName -match '(?i)onedrive') -or
+ (($_.'Task To Run') -and (($_.'Task To Run') -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.Actions) -and ($_.Actions -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.'Task Run') -and (($_.'Task Run') -match '(?i)onedrive(\\.exe)?')) -or
+ (($_.Actions) -and ($_.Actions -match '(?i)OneDriveSetup\.exe|\\OneDrive\.exe')) -or
+ (($_.'Task To Run') -and (($_.'Task To Run') -match '(?i)OneDriveSetup\.exe|\\OneDrive\.exe'))
+ }
+
+ return @($matches | Select-Object -ExpandProperty TaskName -Unique)
+ }
+ catch {
+ Write-Log -message ("OneDriveTasks :: enumerate failed: {0}" -f $_.Exception.Message) -severity 'WARN'
+ return @()
+ }
+ }
+
+ function Test-TaskExists([string]$TaskName) {
+ try {
+ schtasks.exe /Query /TN "$TaskName" 1>$null 2>$null
+ return ($LASTEXITCODE -eq 0)
+ } catch {
+ return $true # assume it exists if we couldn't query
+ }
+ }
+
+ function Remove-TaskWithRetries {
+ param(
+ [Parameter(Mandatory)][string]$TaskName
+ )
+
+ $deadline = (Get-Date).AddSeconds($PerTaskDeleteTimeoutSeconds)
+ $attempt = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $attempt++
+
+ try {
+ schtasks.exe /Delete /TN "$TaskName" /F 2>$null | Out-Null
+ $exit = $LASTEXITCODE
+
+ if ($exit -eq 0) {
+ # Some tasks "delete" but linger briefly; verify
+ if (-not (Test-TaskExists -TaskName $TaskName)) {
+ Write-Log -message ("OneDriveTasks :: deleted {0} (attempt {1})" -f $TaskName, $attempt) -severity 'INFO'
+ return $true
+ }
+
+ Write-Log -message ("OneDriveTasks :: delete reported success but task still exists: {0} (attempt {1})" -f $TaskName, $attempt) -severity 'WARN'
+ } else {
+ Write-Log -message ("OneDriveTasks :: delete failed {0} (exit {1}, attempt {2})" -f $TaskName, $exit, $attempt) -severity 'WARN'
+ }
+ }
+ catch {
+ Write-Log -message ("OneDriveTasks :: exception deleting {0} (attempt {1}): {2}" -f $TaskName, $attempt, $_.Exception.Message) -severity 'WARN'
+ }
+
+ Start-Sleep -Seconds $PerTaskRetryIntervalSeconds
+ }
+
+ Write-Log -message ("OneDriveTasks :: timeout deleting {0} after {1}s" -f $TaskName, $PerTaskDeleteTimeoutSeconds) -severity 'ERROR'
+ return $false
+ }
+
+ Write-Log -message ("OneDriveTasks :: begin (timeout={0}s, interval={1}s, perTaskTimeout={2}s)" -f $TimeoutSeconds, $RetryIntervalSeconds, $PerTaskDeleteTimeoutSeconds) -severity 'DEBUG'
+
+ $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
+ $pass = 0
+
+ while ((Get-Date) -lt $deadline) {
+ $pass++
+ $targets = Get-OneDriveTaskNames
+
+ if (-not $targets -or $targets.Count -eq 0) {
+ Write-Log -message ("OneDriveTasks :: none found (pass {0})" -f $pass) -severity 'INFO'
+ Write-Log -message "OneDriveTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ Write-Log -message ("OneDriveTasks :: pass {0}: found {1} task(s)" -f $pass, $targets.Count) -severity 'INFO'
+
+ foreach ($tn in $targets) {
+ $null = Remove-TaskWithRetries -TaskName $tn
+ }
+
+ # Re-check right away; if still present, sleep and retry until overall timeout
+ $stillThere = Get-OneDriveTaskNames
+ if (-not $stillThere -or $stillThere.Count -eq 0) {
+ Write-Log -message ("OneDriveTasks :: verification OK after pass {0}" -f $pass) -severity 'INFO'
+ Write-Log -message "OneDriveTasks :: end (success)" -severity 'DEBUG'
+ return
+ }
+
+ $remaining = [math]::Max(0, [int]($deadline - (Get-Date)).TotalSeconds)
+ Write-Log -message ("OneDriveTasks :: still present after pass {0} (remaining {1}s). Sleeping {2}s..." -f $pass, $remaining, $RetryIntervalSeconds) -severity 'WARN'
+ Start-Sleep -Seconds $RetryIntervalSeconds
+ }
+
+ $final = Get-OneDriveTaskNames
+ if ($final -and $final.Count -gt 0) {
+ $sample = ($final | Select-Object -First 10) -join '; '
+ Write-Log -message ("OneDriveTasks :: timeout after {0}s. Remaining task(s): {1}" -f $TimeoutSeconds, $sample) -severity 'ERROR'
+ } else {
+ Write-Log -message "OneDriveTasks :: end (success at timeout boundary)" -severity 'INFO'
+ }
+}
+
+Remove-OneDriveScheduledTasks
diff --git a/modules/win_scheduled_tasks/files/self_redeploy_check.ps1 b/modules/win_scheduled_tasks/files/self_redeploy_check.ps1
new file mode 100644
index 000000000..cd7172719
--- /dev/null
+++ b/modules/win_scheduled_tasks/files/self_redeploy_check.ps1
@@ -0,0 +1,359 @@
+<#
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#>
+
+function Write-Log {
+ param (
+ [string] $message,
+ [string] $severity = 'INFO',
+ [string] $source = 'MaintainSystem',
+ [string] $logName = 'Application'
+ )
+ if (!([Diagnostics.EventLog]::Exists($logName)) -or !([Diagnostics.EventLog]::SourceExists($source))) {
+ New-EventLog -LogName $logName -Source $source
+ }
+ switch ($severity) {
+ 'DEBUG' {
+ $entryType = 'SuccessAudit'
+ $eventId = 2
+ break
+ }
+ 'WARN' {
+ $entryType = 'Warning'
+ $eventId = 3
+ break
+ }
+ 'ERROR' {
+ $entryType = 'Error'
+ $eventId = 4
+ break
+ }
+ default {
+ $entryType = 'Information'
+ $eventId = 1
+ break
+ }
+ }
+ Write-EventLog -LogName $logName -Source $source -EntryType $entryType -Category 0 -EventID $eventId -Message $message
+ if ([Environment]::UserInteractive) {
+ $fc = @{ 'Information' = 'White'; 'Error' = 'Red'; 'Warning' = 'DarkYellow'; 'SuccessAudit' = 'DarkGray' }[$entryType]
+ Write-Host -object $message -ForegroundColor $fc
+ }
+}
+function Invoke-DownloadWithRetryGithub {
+ Param(
+ [Parameter(Mandatory)] [string] $Url,
+ [Alias("Destination")] [string] $Path,
+ [string] $PAT
+ )
+ if (-not $Path) {
+ $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
+ $re = "[{0}]" -f [RegEx]::Escape($invalidChars)
+ $fileName = [IO.Path]::GetFileName($Url) -replace $re
+ if ([String]::IsNullOrEmpty($fileName)) { $fileName = [System.IO.Path]::GetRandomFileName() }
+ $Path = Join-Path -Path "${env:Temp}" -ChildPath $fileName
+ }
+ Write-Host "Downloading package from $Url to $Path..."
+ $interval = 30
+ $downloadStartTime = Get-Date
+ for ($retries = 20; $retries -gt 0; $retries--) {
+ try {
+ $attemptStartTime = Get-Date
+ $Headers = @{
+ Accept = "application/vnd.github+json"
+ Authorization = "Bearer $($PAT)"
+ "X-GitHub-Api-Version" = "2022-11-28"
+ }
+ $response = Invoke-WebRequest -Uri $Url -Headers $Headers -OutFile $Path
+ $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)
+ Write-Host "Package downloaded in $attemptSeconds seconds"
+ Write-Host "Status: $($response.statuscode)"
+ break
+ } catch {
+ $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)
+ Write-Warning "Package download failed in $attemptSeconds seconds"
+ Write-Host "Status: $($response.statuscode)"
+ Write-Warning $_.Exception.Message
+ if ($_.Exception.InnerException.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
+ Write-Warning "Request returned 404 Not Found. Aborting download."
+ $retries = 0
+ }
+ }
+ if ($retries -eq 0) {
+ $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)
+ throw "Package download failed after $totalSeconds seconds"
+ }
+ Write-Warning "Waiting $interval seconds before retrying (retries left: $retries)..."
+ Start-Sleep -Seconds $interval
+ }
+ return $Path
+}
+
+# -------------------------------------------------------------------
+# Persistent PXE Pending Flag
+# -------------------------------------------------------------------
+function Set-PXEPendingFlag {
+ param([switch]$Clear)
+
+ $regPath = "HKLM:\SOFTWARE\Mozilla\PXE"
+ $name = "PendingPXE"
+
+ if ($Clear) {
+ if (Test-Path $regPath) {
+ Remove-ItemProperty -Path $regPath -Name $name -ErrorAction SilentlyContinue
+ }
+ return
+ }
+
+ if (-not (Test-Path $regPath)) {
+ New-Item -Path $regPath -Force | Out-Null
+ }
+
+ New-ItemProperty -Path $regPath -Name $name -Value "1" -PropertyType String -Force | Out-Null
+}
+
+function Get-PXEPendingFlag {
+ $regPath = "HKLM:\SOFTWARE\Mozilla\PXE"
+ $name = "PendingPXE"
+
+ if (Test-Path "$regPath\$name") { return $true }
+ return $false
+}
+
+function CompareConfig {
+ param (
+ [string]$yaml_url = "https://raw.githubusercontent.com/mozilla-platform-ops/worker-images/refs/heads/main/provisioners/windows/MDC1Windows/pools.yml",
+ [string]$PAT
+ )
+
+ begin {
+ Write-Log -message ('{0} :: begin - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+
+ # Detect previous deferral due to active task
+ if (Get-PXEPendingFlag) {
+ Write-Log -message "PXE/Reboot pending task completion from previous run." -severity 'INFO'
+ }
+ }
+
+ process {
+ $yaml = $null
+ $SETPXE = $false
+ $yamlHash = $null
+ $IPAddress = $null
+
+ # -------------------------------
+ # Resolve IP
+ # -------------------------------
+ $Ethernet = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() |
+ Where-Object { $_.Name -match "ethernet" }
+
+ try {
+ $IPAddress = ($Ethernet.GetIPProperties().UnicastAddresses |
+ Where-Object { $_.Address.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork -and $_.Address.IPAddressToString -ne "127.0.0.1" } |
+ Select-Object -First 1 -ExpandProperty Address).IPAddressToString
+ }
+ catch {
+ try {
+ $NetshOutput = netsh interface ip show addresses
+ $IPAddress = ($NetshOutput -match "IP Address" | ForEach-Object {
+ if ($_ -notmatch "127.0.0.1") { $_ -replace ".*?:\s*", "" }
+ })[0]
+ }
+ catch {
+ Write-Log -message "Failed to get IP address" -severity 'ERROR'
+ }
+ }
+
+ if ($IPAddress) {
+ Write-Log -message "IP Address: $IPAddress" -severity 'INFO'
+ }
+ else {
+ Write-Log -message "No IP Address could be determined." -severity 'ERROR'
+ return
+ }
+
+ try {
+ $ResolvedName = (Resolve-DnsName -Name $IPAddress -Server "10.48.75.120").NameHost
+ }
+ catch {
+ Write-Log -message "DNS resolution failed." -severity 'ERROR'
+ return
+ }
+
+ Write-Log -message "Resolved Name: $ResolvedName" -severity 'INFO'
+
+ $index = $ResolvedName.IndexOf('.')
+ if ($index -lt 0) {
+ Write-Log -message "Invalid hostname format." -severity 'ERROR'
+ return
+ }
+
+ $worker_node_name = $ResolvedName.Substring(0, $index)
+ Write-Log -message "Host name set to: $worker_node_name" -severity 'INFO'
+
+ $localHash = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).GITHASH
+ $localPool = (Get-ItemProperty -Path HKLM:\SOFTWARE\Mozilla\ronin_puppet).worker_pool_id
+
+ $patFile = "D:\Secrets\pat.txt"
+ if (-not (Test-Path $patFile)) {
+ Write-Log -message ('{0} :: PAT file missing: {1}' -f $MyInvocation.MyCommand.Name, $patFile) -severity 'ERROR'
+ Set-PXE
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+
+ $tempYamlPath = "$env:TEMP\pools.yml"
+ $PAT = Get-Content $patFile -ErrorAction Stop
+
+ $splat = @{
+ Url = $yaml_url
+ Path = $tempYamlPath
+ PAT = $PAT
+ }
+
+ if (-not (Invoke-DownloadWithRetryGithub @splat)) {
+ Write-Log -message ('{0} :: YAML download failed after retries. PXE rebooting.' -f $MyInvocation.MyCommand.Name) -severity 'ERROR'
+ Set-PXE
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+
+ try {
+ $yaml = Get-Content $tempYamlPath -Raw | ConvertFrom-Yaml
+ }
+ catch {
+ Write-Log -message ('{0} :: YAML parsing failed: {1}' -f $MyInvocation.MyCommand.Name, $_) -severity 'ERROR'
+ Set-PXE
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+
+ $found = $false
+ if ($yaml) {
+ foreach ($pool in $yaml.pools) {
+ foreach ($node in $pool.nodes) {
+ if ($node -eq $worker_node_name) {
+ $WorkerPool = $pool.name
+ $yamlHash = $pool.hash
+ $yamlImageName = $pool.image
+ $yamlImageDir = "D:\" + $yamlImageName
+ $found = $true
+ break
+ }
+ }
+ if ($found) { break }
+ }
+ }
+
+ if (-not $found) {
+ Write-Log -message "Node name not found in YAML!!" -severity 'ERROR'
+ # $SETPXE = $true
+ }
+
+ Write-Log -message "=== Configuration Comparison ===" -severity 'INFO'
+
+ if ($localPool -eq $WorkerPool) {
+ Write-Log -message "Worker Pool Match: $WorkerPool" -severity 'INFO'
+ }
+ else {
+ Write-Log -message "Worker Pool MISMATCH!" -severity 'ERROR'
+ #$SETPXE = $true
+ }
+
+ if ([string]::IsNullOrWhiteSpace($yamlHash)) {
+ Write-Log -message "YAML hash is missing or invalid. Treating as mismatch." -severity 'ERROR'
+ $SETPXE = $true
+ }
+ elseif ($localHash -ne $yamlHash) {
+ Write-Log -message "Git Hash MISMATCH!" -severity 'ERROR'
+ Write-Log -message "Local: $localHash" -severity 'WARN'
+ Write-Log -message "YAML : $yamlHash" -severity 'WARN'
+ $SETPXE = $true
+ }
+ else {
+ Write-Log -message "Git Hash Match: $yamlHash" -severity 'INFO'
+ }
+
+ if (!(Test-Path $yamlImageDir)) {
+ Write-Log -message "Image Directory MISMATCH! YAML: $yamlImageDir NOT FOUND" -severity 'ERROR'
+ $SETPXE = $true
+ }
+
+ if ($SETPXE) {
+
+ Write-Log -message "Configuration mismatch detected. Evaluating worker-status.json..." -severity 'WARN'
+
+ $searchPaths = @(
+ "C:\WINDOWS\SystemTemp",
+ $env:TMP,
+ $env:TEMP,
+ $env:USERPROFILE
+ )
+
+ $workerStatus = $null
+ foreach ($path in $searchPaths) {
+ if ($null -ne $path) {
+ $candidate = Join-Path $path "worker-status.json"
+ if (Test-Path $candidate) {
+ $workerStatus = $candidate
+ break
+ }
+ }
+ }
+
+ if (-not $workerStatus) {
+ Write-Log -message "worker-status.json not found. Rebooting now!" -severity 'ERROR'
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+
+ try {
+ $json = Get-Content $workerStatus -Raw | ConvertFrom-Json
+ }
+ catch {
+ Write-Log -message "worker-status.json is unreadable. Rebooting now!" -severity 'ERROR'
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+
+ if (($json.currentTaskIds).Count -eq 0) {
+ Write-Log -message "No active tasks. Rebooting now!" -severity 'WARN'
+ Set-PXEPendingFlag -Clear
+ Restart-Computer -Force
+ return
+ }
+ else {
+ $task = $json.currentTaskIds[0]
+ Write-Log -message "Task $task is active. PXE/Reboot deferred until task completion." -severity 'INFO'
+
+ # Record pending reboot/PXE
+ Set-PXEPendingFlag
+
+ # Prepare PXE boot
+ Set-PXE
+ return
+ }
+ }
+
+ Write-Log -message "SETPXE set to: $SETPXE" -severity 'DEBUG'
+ }
+
+ end {
+ Write-Log -message ('{0} :: end - {1:o}' -f $($MyInvocation.MyCommand.Name), (Get-Date).ToUniversalTime()) -severity 'DEBUG'
+ }
+}
+
+$bootstrap_stage = (Get-ItemProperty -path "HKLM:\SOFTWARE\Mozilla\ronin_puppet").bootstrap_stage
+If ($bootstrap_stage -eq 'complete') {
+ CompareConfig
+} else {
+ Write-Log -message ('{0} :: Bootstrap has not completed. EXITING!' -f $($MyInvocation.MyCommand.Name)) -severity 'DEBUG'
+ Exit-PSSession
+}
diff --git a/modules/win_scheduled_tasks/manifests/gw_exe_check.pp b/modules/win_scheduled_tasks/manifests/gw_exe_check.pp
index 833ba77a4..5aa5c994d 100644
--- a/modules/win_scheduled_tasks/manifests/gw_exe_check.pp
+++ b/modules/win_scheduled_tasks/manifests/gw_exe_check.pp
@@ -5,22 +5,23 @@
class win_scheduled_tasks::gw_exe_check (
) {
- $gw_exe_check_ps1 = "${facts['custom_win_roninprogramdata']}\\gw_exe_check.ps1"
+ $gw_exe_check_ps1 = "${facts['custom_win_roninprogramdata']}\\gw_exe_check.ps1"
- file { $gw_exe_check_ps1:
- content => file('win_scheduled_tasks/gw_exe_check.ps1'),
- }
- # Resource from puppetlabs-scheduled_task
- scheduled_task { 'gw_exe_check':
- ensure => 'present',
- command => "${facts['custom_win_system32']}\\WindowsPowerShell\\v1.0\\powershell.exe",
- arguments => "-executionpolicy bypass -File ${gw_exe_check_ps1}",
- enabled => true,
- trigger => [{
- 'schedule' => 'boot',
- 'minutes_interval' => '0',
- 'minutes_duration' => '0'
- }],
- user => 'system',
- }
+ file { $gw_exe_check_ps1:
+ content => file('win_scheduled_tasks/gw_exe_check.ps1'),
+ }
+
+ scheduled_task { 'gw_exe_check':
+ ensure => 'present',
+ command => "${facts['custom_win_system32']}\\WindowsPowerShell\\v1.0\\powershell.exe",
+ arguments => "-executionpolicy bypass -File ${gw_exe_check_ps1}",
+ enabled => true,
+ trigger => [{
+ schedule => 'daily',
+ start_time => '00:00',
+ minutes_interval => 60,
+ #minutes_duration => 1440, # 24 hours = repeat every 5 minutes all day
+ }],
+ user => 'SYSTEM',
+ }
}
diff --git a/modules/win_scheduled_tasks/manifests/onedrive_task_deletion.pp b/modules/win_scheduled_tasks/manifests/onedrive_task_deletion.pp
new file mode 100644
index 000000000..c00e2998a
--- /dev/null
+++ b/modules/win_scheduled_tasks/manifests/onedrive_task_deletion.pp
@@ -0,0 +1,26 @@
+class win_scheduled_tasks::onedrive_task_deletion {
+ $onedrive_task_deletion_ps = "${facts['custom_win_roninprogramdata']}\\onedrive_task_deletion.ps1"
+
+ if $facts['os']['name'] == 'Windows' {
+
+ file { $onedrive_task_deletion_ps:
+ ensure => file,
+ content => file('win_scheduled_tasks/onedrive_task_deletion.ps1'),
+ }
+
+ scheduled_task { 'one_drive_task_deletion':
+ ensure => present,
+ command => "${facts['custom_win_system32']}\\WindowsPowerShell\\v1.0\\powershell.exe",
+ arguments => "-executionpolicy bypass -File \"${onedrive_task_deletion_ps}\"",
+ enabled => true,
+ trigger => [{
+ schedule => boot,
+ }],
+ user => 'SYSTEM',
+ require => File[$onedrive_task_deletion_ps],
+ }
+
+ } else {
+ fail("${module_name} does not support ${facts['os']['name']}")
+ }
+}
diff --git a/modules/win_scheduled_tasks/manifests/self_redeploy_check.pp b/modules/win_scheduled_tasks/manifests/self_redeploy_check.pp
new file mode 100644
index 000000000..271fb5885
--- /dev/null
+++ b/modules/win_scheduled_tasks/manifests/self_redeploy_check.pp
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+class win_scheduled_tasks::self_redeploy_check (
+) {
+
+ $script = "${facts['custom_win_roninprogramdata']}\\self_redeploy_check.ps1"
+
+ file { $script:
+ content => file('win_scheduled_tasks/self_redeploy_check.ps1'),
+ }
+
+ scheduled_task { 'self_redeploy_check':
+ ensure => 'present',
+ command => "${facts['custom_win_system32']}\\WindowsPowerShell\\v1.0\\powershell.exe",
+ arguments => "-executionpolicy bypass -File ${script}",
+ enabled => true,
+ trigger => [{
+ schedule => 'daily',
+ start_time => '00:00',
+ minutes_interval => 120,
+ #minutes_interval => 5,
+ #minutes_duration => 1440, # 24 hours = repeat every 2 hours all day
+ }],
+ user => 'SYSTEM',
+ }
+}