Skip to content

feat: Add Intune detection and setup scripts for Windows deployment#144

Open
bmsimp wants to merge 1 commit intoCyberDrain:devfrom
bmsimp:feat/intune-detection-setup
Open

feat: Add Intune detection and setup scripts for Windows deployment#144
bmsimp wants to merge 1 commit intoCyberDrain:devfrom
bmsimp:feat/intune-detection-setup

Conversation

@bmsimp
Copy link
Copy Markdown
Member

@bmsimp bmsimp commented Apr 10, 2026

Summary

  • Adds Detect-Windows-Chrome-and-Edge.ps1 -- an Intune detection script that verifies all registry keys written by the deploy script match expected configuration. Exits 0 (compliant) or 1 (drift detected), enabling Intune to automatically redeploy when settings change.
  • Adds Setup-Windows-Chrome-and-Edge.ps1 -- an interactive configurator that downloads the latest Deploy, Remove, and Detect scripts from GitHub, walks the user through each setting with defaults and validation, and outputs ready-to-upload scripts for Intune.
  • Updates domain-deployment.md with Intune-specific deployment instructions.

All config blocks mirror the existing Deploy-Windows-Chrome-and-Edge.ps1 variable names, grouping, and comment style for consistency across deployment methods.

Details

Detection script features:

  • Config block identical to the deploy script (same variables, same 0/1 convention, same inline comments)
  • Test-RegValue helper checks each registry property against expected values
  • Verifies both Chrome and Edge: core settings, domain squatting, custom branding, generic webhook (events + URL allowlist with exact count), extension settings, and conditional toolbar pin
  • Array subkeys (urlAllowlist, webhookEvents\events) verified bidirectionally -- checks count matches and no stale entries exist

Setup script features:

  • Downloads latest templates from GitHub with 30s timeout
  • Conditional prompts -- skips webhook URL/events when webhooks disabled, skips CIPP URL/tenant when CIPP reporting disabled
  • Input validation: 0/1 for booleans, 1-168 range for update interval, rejects double-quote characters in string fields
  • URL allowlist entered one at a time (not comma-separated)
  • Outputs configured scripts to a user-chosen directory

Copilot AI review requested due to automatic review settings April 10, 2026 20:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Windows Intune-focused tooling for deploying the Check browser extension by introducing a registry-based detection script, an interactive setup script that generates configured deploy/remove/detect scripts, and updating the Windows domain deployment documentation accordingly.

Changes:

  • Added an Intune detection script that validates Chrome/Edge policy registry keys against expected configuration.
  • Added an interactive setup script that downloads templates and generates configured Deploy/Remove/Detect scripts for upload to Intune.
  • Updated Windows domain deployment docs with Win32 app + detection-script guidance for Intune.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
enterprise/Setup-Windows-Chrome-and-Edge.ps1 Interactive generator that downloads template scripts and applies config replacements.
enterprise/Detect-Windows-Chrome-and-Edge.ps1 Intune detection script that checks registry policy keys/values for Chrome and Edge.
docs/deployment/chrome-edge-deployment-instructions/windows/domain-deployment.md Updated documentation to describe Intune Win32 app packaging, detection rules, and troubleshooting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +29 to +33
Write-Host "Downloading latest scripts from GitHub..." -ForegroundColor Yellow
$templates = @{}
foreach ($key in $scripts.Keys) {
try {
$templates[$key] = Invoke-WebRequest -Uri $scripts[$key].Url -UseBasicParsing -TimeoutSec 30 | Select-Object -ExpandProperty Content
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoke-WebRequest -UseBasicParsing will fail on PowerShell 6/7+ (the parameter was removed). Since this setup script is intended to be run interactively on an admin workstation, consider removing -UseBasicParsing (it’s not needed on modern PowerShell) or adding a version check/fallback so downloads work in both Windows PowerShell 5.1 and PowerShell 7.

Suggested change
Write-Host "Downloading latest scripts from GitHub..." -ForegroundColor Yellow
$templates = @{}
foreach ($key in $scripts.Keys) {
try {
$templates[$key] = Invoke-WebRequest -Uri $scripts[$key].Url -UseBasicParsing -TimeoutSec 30 | Select-Object -ExpandProperty Content
function Invoke-DownloadWebRequest {
param (
[string]$Uri
)
if ($PSVersionTable.PSVersion.Major -ge 6) {
return Invoke-WebRequest -Uri $Uri -TimeoutSec 30
}
return Invoke-WebRequest -Uri $Uri -UseBasicParsing -TimeoutSec 30
}
Write-Host "Downloading latest scripts from GitHub..." -ForegroundColor Yellow
$templates = @{}
foreach ($key in $scripts.Keys) {
try {
$templates[$key] = Invoke-DownloadWebRequest -Uri $scripts[$key].Url | Select-Object -ExpandProperty Content

Copilot uses AI. Check for mistakes.
function Format-ArrayLiteral {
param ([string[]]$Values)
if ($Values.Count -eq 0) { return '@()' }
$quoted = $Values | ForEach-Object { $escaped = $_ -replace '"', '""'; "`"$escaped`"" }
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Format-ArrayLiteral builds array elements using double-quoted string literals, which means $ and backtick characters in allowlist/regex entries (common in regex like $ end-of-line anchors) will be interpreted/expanded when the generated Deploy/Detect scripts run. This can silently corrupt regex/URLs and break policy. Consider generating single-quoted literals (escaping embedded ' by doubling) or explicitly escaping $/` in values before writing them into the scripts.

Suggested change
$quoted = $Values | ForEach-Object { $escaped = $_ -replace '"', '""'; "`"$escaped`"" }
$quoted = $Values | ForEach-Object { $escaped = $_ -replace "'", "''"; "'$escaped'" }

Copilot uses AI. Check for mistakes.
Comment on lines +218 to +226
@{ Pattern = '$cippServerUrl = "" #'; Value = "`$cippServerUrl = `"$cfg_cippServerUrl`" #" }
@{ Pattern = '$cippTenantId = "" #'; Value = "`$cippTenantId = `"$cfg_cippTenantId`" #" }
@{ Pattern = '$customRulesUrl = "" #'; Value = "`$customRulesUrl = `"$cfg_customRulesUrl`" #" }
@{ Pattern = '$updateInterval = 24 #'; Value = "`$updateInterval = $cfg_updateInterval #" }
@{ Pattern = '$domainSquattingEnabled = 1 #'; Value = "`$domainSquattingEnabled = $cfg_domainSquattingEnabled #" }
@{ Pattern = '$enableDebugLogging = 0 #'; Value = "`$enableDebugLogging = $cfg_enableDebugLogging #" }
@{ Pattern = '$enableGenericWebhook = 0 #'; Value = "`$enableGenericWebhook = $cfg_enableGenericWebhook #" }
@{ Pattern = '$webhookUrl = "" #'; Value = "`$webhookUrl = `"$cfg_webhookUrl`" #" }
@{ Pattern = '$companyName = "CyberDrain" #'; Value = "`$companyName = `"$cfg_companyName`" #" }
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replacement map writes user-provided values into double-quoted literals (e.g., for URLs and branding fields) without escaping $ or backticks, so values containing $ (query params, regex fragments, etc.) can be expanded at runtime in the generated scripts. Escaping these characters (or switching to single-quoted literals consistently) would prevent unintended interpolation and make generated scripts match the intended literal values.

Copilot uses AI. Check for mistakes.
Comment on lines +163 to +165
# Toolbar pin — only checked when enabled (upstream install script does not write this property when disabled)
if ($forceToolbarPin -eq 1) {
if (!(Test-RegValue $browser.ExtensionSettingsKey $browser.ToolbarProp $browser.ToolbarValue)) { exit 1 }
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When $forceToolbarPin is set to 0, the detection script does not verify that the browser-specific toolbar policy value is absent. If a device was previously deployed with pinning enabled, the stale toolbar_pin/toolbar_state value can remain and this script will still report compliant, so Intune won’t remediate the change. Consider explicitly failing when the toolbar property exists while $forceToolbarPin -eq 0 (and ensure the deploy script removes the property in that case to avoid a reinstall loop).

Suggested change
# Toolbar pin — only checked when enabled (upstream install script does not write this property when disabled)
if ($forceToolbarPin -eq 1) {
if (!(Test-RegValue $browser.ExtensionSettingsKey $browser.ToolbarProp $browser.ToolbarValue)) { exit 1 }
# Toolbar pin — when enabled, require the expected value; when disabled, require the property to be absent
if ($forceToolbarPin -eq 1) {
if (!(Test-RegValue $browser.ExtensionSettingsKey $browser.ToolbarProp $browser.ToolbarValue)) { exit 1 }
} else {
$extensionSettingsProps = (Get-Item $browser.ExtensionSettingsKey).Property
if ($null -ne $extensionSettingsProps -and $extensionSettingsProps -contains $browser.ToolbarProp) { exit 1 }

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +114
foreach ($browser in $browsers) {
# Verify managed storage key exists
if (!(Test-Path $browser.ManagedStorageKey)) { exit 1 }

$policyKey = $browser.ManagedStorageKey

# Core DWord settings
if (!(Test-RegValue $policyKey 'showNotifications' $showNotifications)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableValidPageBadge' $enableValidPageBadge)) { exit 1 }
if (!(Test-RegValue $policyKey 'enablePageBlocking' $enablePageBlocking)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableCippReporting' $enableCippReporting)) { exit 1 }
if (!(Test-RegValue $policyKey 'updateInterval' $updateInterval)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableDebugLogging' $enableDebugLogging)) { exit 1 }

# Core String settings
if (!(Test-RegValue $policyKey 'cippServerUrl' $cippServerUrl)) { exit 1 }
if (!(Test-RegValue $policyKey 'cippTenantId' $cippTenantId)) { exit 1 }
if (!(Test-RegValue $policyKey 'customRulesUrl' $customRulesUrl)) { exit 1 }

# domainSquatting subkey
$domainSquattingKey = "$policyKey\domainSquatting"
if (!(Test-Path $domainSquattingKey)) { exit 1 }
if (!(Test-RegValue $domainSquattingKey 'enabled' $domainSquattingEnabled)) { exit 1 }

# customBranding subkey
$brandingKey = "$policyKey\customBranding"
if (!(Test-Path $brandingKey)) { exit 1 }
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script exits 1 on the first mismatch but doesn’t emit any details about what key/value failed. The updated docs recommend running it manually to see which check fails, but without output that’s hard to diagnose. Consider printing a short message (browser + key + expected/actual) before each failure, or refactoring to accumulate failures and output a summary before exiting 1.

Suggested change
foreach ($browser in $browsers) {
# Verify managed storage key exists
if (!(Test-Path $browser.ManagedStorageKey)) { exit 1 }
$policyKey = $browser.ManagedStorageKey
# Core DWord settings
if (!(Test-RegValue $policyKey 'showNotifications' $showNotifications)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableValidPageBadge' $enableValidPageBadge)) { exit 1 }
if (!(Test-RegValue $policyKey 'enablePageBlocking' $enablePageBlocking)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableCippReporting' $enableCippReporting)) { exit 1 }
if (!(Test-RegValue $policyKey 'updateInterval' $updateInterval)) { exit 1 }
if (!(Test-RegValue $policyKey 'enableDebugLogging' $enableDebugLogging)) { exit 1 }
# Core String settings
if (!(Test-RegValue $policyKey 'cippServerUrl' $cippServerUrl)) { exit 1 }
if (!(Test-RegValue $policyKey 'cippTenantId' $cippTenantId)) { exit 1 }
if (!(Test-RegValue $policyKey 'customRulesUrl' $customRulesUrl)) { exit 1 }
# domainSquatting subkey
$domainSquattingKey = "$policyKey\domainSquatting"
if (!(Test-Path $domainSquattingKey)) { exit 1 }
if (!(Test-RegValue $domainSquattingKey 'enabled' $domainSquattingEnabled)) { exit 1 }
# customBranding subkey
$brandingKey = "$policyKey\customBranding"
if (!(Test-Path $brandingKey)) { exit 1 }
function Write-DetectionFailure {
param(
[string]$BrowserName,
[string]$KeyPath,
[string]$ValueName,
[object]$ExpectedValue,
[object]$ActualValue
)
if ([string]::IsNullOrEmpty($ValueName)) {
Write-Output "$BrowserName detection failed: missing registry key '$KeyPath'."
return
}
Write-Output "$BrowserName detection failed for '$ValueName' at '$KeyPath': expected '$ExpectedValue', actual '$ActualValue'."
}
function Test-RegValueWithDetails {
param(
[string]$BrowserName,
[string]$KeyPath,
[string]$ValueName,
[object]$ExpectedValue
)
$matches = Test-RegValue $KeyPath $ValueName $ExpectedValue
if ($matches) {
return $true
}
$actualValue = '<missing>'
if (Test-Path $KeyPath) {
try {
$property = Get-ItemProperty -Path $KeyPath -Name $ValueName -ErrorAction Stop
$actualValue = $property.$ValueName
}
catch {
$actualValue = '<missing>'
}
}
Write-DetectionFailure -BrowserName $BrowserName -KeyPath $KeyPath -ValueName $ValueName -ExpectedValue $ExpectedValue -ActualValue $actualValue
return $false
}
foreach ($browser in $browsers) {
# Verify managed storage key exists
if (!(Test-Path $browser.ManagedStorageKey)) {
Write-DetectionFailure -BrowserName $browser.Name -KeyPath $browser.ManagedStorageKey -ValueName $null -ExpectedValue $null -ActualValue $null
exit 1
}
$policyKey = $browser.ManagedStorageKey
# Core DWord settings
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'showNotifications' $showNotifications)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'enableValidPageBadge' $enableValidPageBadge)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'enablePageBlocking' $enablePageBlocking)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'enableCippReporting' $enableCippReporting)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'updateInterval' $updateInterval)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'enableDebugLogging' $enableDebugLogging)) { exit 1 }
# Core String settings
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'cippServerUrl' $cippServerUrl)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'cippTenantId' $cippTenantId)) { exit 1 }
if (!(Test-RegValueWithDetails $browser.Name $policyKey 'customRulesUrl' $customRulesUrl)) { exit 1 }
# domainSquatting subkey
$domainSquattingKey = "$policyKey\domainSquatting"
if (!(Test-Path $domainSquattingKey)) {
Write-DetectionFailure -BrowserName $browser.Name -KeyPath $domainSquattingKey -ValueName $null -ExpectedValue $null -ActualValue $null
exit 1
}
if (!(Test-RegValueWithDetails $browser.Name $domainSquattingKey 'enabled' $domainSquattingEnabled)) { exit 1 }
# customBranding subkey
$brandingKey = "$policyKey\customBranding"
if (!(Test-Path $brandingKey)) {
Write-DetectionFailure -BrowserName $browser.Name -KeyPath $brandingKey -ValueName $null -ExpectedValue $null -ActualValue $null
exit 1
}

Copilot uses AI. Check for mistakes.

* **Installation policy** → tells the browser to force-install the extension.
* **Configuration policy** → applies your custom extension settings.
The simplest method of Intune deployment is via a win32 script. Follow the steps below to:
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction sentence is incomplete: “Follow the steps below to:” ends without listing what the reader will accomplish. Consider finishing the sentence (or replacing with a short bullet list) so the section reads cleanly.

Suggested change
The simplest method of Intune deployment is via a win32 script. Follow the steps below to:
The simplest method of Intune deployment is via a win32 script. Follow the steps below to deploy Check with Intune.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +44
### Step 1: Package the Scripts

Intune Win32 apps require an `.intunewin` package. Place your three configured scripts in a folder, then run:

```powershell
.\IntuneWinAppUtil.exe -c "C:\path\to\scripts\folder" -s "Deploy-Windows-Chrome-and-Edge.ps1" -o "C:\path\to\output"
```

This creates `Deploy-Windows-Chrome-and-Edge.intunewin`.

### Step 3: Configure App Information

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step numbering skips from “Step 1” to “Step 3” in the Intune instructions. Renumbering to be sequential (or adding the missing Step 2) will reduce confusion when following the guide.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +78
| Run script in 64-bit PowerShell host | **No** |

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Detection rules table includes “Run script in 64-bit PowerShell host”, which (depending on the Intune Win32 app UI) may not be an available setting for custom detection scripts. Also, if a 64-bit/32-bit choice is available, this detection script reads HKLM:\SOFTWARE\Policies\... and should run in 64-bit to avoid WOW6432Node redirection. Please verify the correct Intune options here and adjust the table accordingly.

Suggested change
| Run script in 64-bit PowerShell host | **No** |
Keep **Run script as 32-bit process on 64-bit clients** set to **No** so the detection script runs in the 64-bit PowerShell/registry context on 64-bit devices. This is important because the script checks values under `HKLM:\SOFTWARE\Policies\...`; running it as 32-bit could read redirected `WOW6432Node` paths and cause detection to fail incorrectly.

Copilot uses AI. Check for mistakes.
2. Navigate to: **Devices → Configuration profiles**
3. Click on **Create → Import Policy**
4. Import the following file to deploy the extensions. This will deploy the configuration
<a href="https://raw.githubusercontent.com/CyberDrain/Check/refs/heads/main/enterprise/Setup-Windows-Chrome-and-Edge.ps1" class="button primary">Import File</a>
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The button text says “Import File”, but the link points to a raw .ps1 download. Renaming the button to something like “Download script” would better match what the action actually does.

Suggested change
<a href="https://raw.githubusercontent.com/CyberDrain/Check/refs/heads/main/enterprise/Setup-Windows-Chrome-and-Edge.ps1" class="button primary">Import File</a>
<a href="https://raw.githubusercontent.com/CyberDrain/Check/refs/heads/main/enterprise/Setup-Windows-Chrome-and-Edge.ps1" class="button primary">Download script</a>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants