Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ release notes.

### Fixed

- **`Repair-IntuneScript` analyzed every script under the inferred context and architecture**, whatever the
caller deployed to, because it had no `-Context`, `-Architecture` or `-EnforceSignatureCheck` to pass on.
It takes the three now and hands them to both analyses, so its findings, fixes and `Remaining` count are
the ones `Test-IntuneScript` gives for the same options.
- **`-Credential` found no session on Windows Home editions, and matched by name.** The session
list came from parsing `query user`, which Home editions do not ship (every run there fell back
to the stored-password task) and which prints localized text. Sessions now come from the owners
Expand Down
16 changes: 15 additions & 1 deletion Public/Repair-IntuneScript.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ function Repair-IntuneScript {
[ValidateSet('Auto', 'Detection', 'Remediation', 'PlatformScript', 'Win32Detection', 'Win32Requirement')]
[string]$ScriptType = 'Auto',

[ValidateSet('Auto', 'System', 'User')]
[string]$Context = 'Auto',

[ValidateSet('Auto', 'x86', 'x64', 'arm64')]
[string]$Architecture = 'Auto',

[string[]]$IncludeRule,

[string[]]$ExcludeRule,

[switch]$EnforceSignatureCheck,

$Settings
)

Expand All @@ -34,7 +42,13 @@ function Repair-IntuneScript {
}

foreach ($file in $files) {
$testSplat = @{ Path = $file; ScriptType = $ScriptType }
# The analysis sees the script the way the caller deploys it, so the findings, their
# fixes and the Remaining count are the ones Test-IntuneScript would give for the same
# options
$testSplat = @{
Path = $file; ScriptType = $ScriptType; Context = $Context; Architecture = $Architecture
}
if ($EnforceSignatureCheck) { $testSplat.EnforceSignatureCheck = $true }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Added to the splat only when set: Test-IntuneScript resolves an unset switch through the directive and the settings file, and a literal $false here would override both.

if ($IncludeRule) { $testSplat.IncludeRule = $IncludeRule }
if ($ExcludeRule) { $testSplat.ExcludeRule = $ExcludeRule }
if ($PSBoundParameters.ContainsKey('Settings')) { $testSplat.Settings = $Settings }
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ Three findings come with an edit the tool can make for you, each behaviour-prese
script-scope `return` becomes the `exit 0` it already produced, with any returned value written
first (`return 'ok'` to `'ok'; exit 0`); a UTF-16 or BOM-less non-ASCII file is rewritten as UTF-8
with a BOM; a padded requirement value (`' ok '`) is trimmed. `Repair-IntuneScript` applies them
per script, reports what it changed and how many findings remain, and previews with `-WhatIf`.
per script, reports what it changed and how many findings remain, previews with `-WhatIf`, and
takes `-ScriptType`, `-Context`, `-Architecture` and `-EnforceSignatureCheck` the way `Test-IntuneScript` does.
Whether that `exit 0` should have been an `exit 1` is still the author's call, which is why the
finding stays an error until the intent is made explicit. Where the script already says which exit
was meant, a `return` with an exit other than 0 after it in the same block (`return 1; exit 1`),
Expand Down
31 changes: 31 additions & 0 deletions Tests/Unit/Public/Repair-IntuneScript.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,37 @@ Describe 'Repair-IntuneScript' -Tag 'Unit', 'Public' {
$fixed.Applied | Should-Be 1
}

It 'hands -Context and -EnforceSignatureCheck to the analysis, on both runs' {
# Without them the repair analyzed every script under the inferred context whatever the
# caller deployed to, so Remaining could disagree with Test-IntuneScript run with the
# same options. HKCU: and USERPROFILE are two errors under System (REM-PROBE-SYS64) and
# under User the rule gives one note; an unsigned Win32 detection is an error only with
# the signature check enforced
$body = "Get-ItemProperty HKCU:\Software\Contoso`nTest-Path `$env:USERPROFILE\x`nexit 1"
$path = New-TestScript 'Remediations\F\Detect.ps1' $body -Bom
$asUser = Repair-IntuneScript -Path $path -Context User -IncludeRule IslContextIssue

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Limited to IslContextIssue because the rule gives one note under User (the Entra-join reminder) in place of the two System errors, so the total counts alone would be equal and prove nothing; the rule on its own gives 1 against 2.

$asSystem = Repair-IntuneScript -Path $path -Context System -IncludeRule IslContextIssue
$asUser.Remaining | Should-Be 1
$asSystem.Remaining | Should-Be 2

$unsigned = New-TestScript 'Win32\F\Detect-App.ps1' "if (Test-Path C:\x) { exit 0 }`nexit 1" -Bom
$enforced = Repair-IntuneScript -Path $unsigned -ScriptType Win32Detection -EnforceSignatureCheck
$plain = Repair-IntuneScript -Path $unsigned -ScriptType Win32Detection
$enforced.Remaining | Should-Be ($plain.Remaining + 1)
}

It 'counts the architecture the caller names, not the inferred one, in Remaining' {
# x86 is the portal default for a remediation, so the 32-bit System32 finding is there
# under Auto and gone under -Architecture x64 (REM-PROBE-SYS32)
$body = "Start-Process C:\Windows\System32\msiexec.exe -Wait`nexit 0"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

A remediation infers x86, the portal default, where System32 is redirected (REM-PROBE-SYS32); -Architecture x64 is the native host and the finding goes. The third assertion compares the two repairs, so a change in the rule's other findings cannot hide a pass-through regression.

$folder = New-TestScript 'Remediations\G\Detect.ps1' $body -Bom
$inferred = Repair-IntuneScript -Path $folder
$native = Repair-IntuneScript -Path $folder -Architecture x64
$inferred.Remaining | Should-Be (@(Test-IntuneScript -Path $folder).Count)
$native.Remaining | Should-Be (@(Test-IntuneScript -Path $folder -Architecture x64).Count)
$native.Remaining | Should-BeLessThan $inferred.Remaining
}

It 'expands a folder and reports one object per script' {
$folder = Join-Path $TestDrive 'Tree'
New-TestScript 'Tree\Remediations\One\Detect.ps1' "if (`$a) { return 'a' }`nexit 1" -Bom | Out-Null
Expand Down
88 changes: 84 additions & 4 deletions docs/IntuneScriptLab/Repair-IntuneScript.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
---
document type: cmdlet
external help file: IntuneScriptLab-Help.xml
HelpUri: https://github.com/fadwen/IntuneScriptLab/blob/main/docs/IntuneScriptLab/Repair-IntuneScript.md
Locale: en-US
Module Name: IntuneScriptLab
ms.date: 10/05/2026
ms.date: 10/06/2026
PlatyPS schema version: 2024-05-01
title: Repair-IntuneScript
---
Expand All @@ -20,8 +20,9 @@ Applies the mechanical fixes for findings that have one, and reports what is lef
### __AllParameterSets

```
Repair-IntuneScript [-Path] <string[]> [-ScriptType <string>] [-IncludeRule <string[]>]
[-ExcludeRule <string[]>] [-Settings <Object>] [-WhatIf] [-Confirm] [<CommonParameters>]
Repair-IntuneScript [-Path] <string[]> [-ScriptType <string>] [-Context <string>]
[-Architecture <string>] [-IncludeRule <string[]>] [-ExcludeRule <string[]>]
[-EnforceSignatureCheck] [-Settings <Object>] [-WhatIf] [-Confirm]
```

## ALIASES
Expand Down Expand Up @@ -71,8 +72,40 @@ Get-ChildItem .\Win32 -Recurse -Filter Requirement*.ps1 | Repair-IntuneScript -I

Trims padded requirement values only, in every requirement script under Win32.

### EXAMPLE 4

Repair-IntuneScript -Path .\Remediations -Architecture x64 -Context System

Repairs the scripts as deployed to the 64-bit host in system context, so the findings that depend
on either, and the Remaining count, match Test-IntuneScript run with the same options.

## PARAMETERS

### -Architecture

The host the script runs in, passed to the analysis: x86 (portal default for scripts and
remediations), x64 (Win32 detection default) or arm64. Auto (default) infers per script as
Test-IntuneScript does. The findings an architecture decides, System32 against Sysnative among
them, and the fixes and Remaining count that follow from them, are then the ones
Test-IntuneScript gives for the same value.

```yaml
Type: System.String
DefaultValue: ''
SupportsWildcards: false
Aliases: []
ParameterSets:
- Name: (All)
Position: Named
IsRequired: false
ValueFromPipeline: false
ValueFromPipelineByPropertyName: false
ValueFromRemainingArguments: false
DontShow: false
AcceptedValues: []
HelpMessage: ''
```

### -Confirm

Prompts you for confirmation before running the cmdlet.
Expand All @@ -95,6 +128,53 @@ AcceptedValues: []
HelpMessage: ''
```

### -Context

System or User, passed to the analysis. Auto (default) uses the directive or the type's portal
default, as Test-IntuneScript does. HKCU: and the profile variables are errors under System and
not under User, so Remaining follows the context the script is deployed in.

```yaml
Type: System.String
DefaultValue: ''
SupportsWildcards: false
Aliases: []
ParameterSets:
- Name: (All)
Position: Named
IsRequired: false
ValueFromPipeline: false
ValueFromPipelineByPropertyName: false
ValueFromRemainingArguments: false
DontShow: false
AcceptedValues: []
HelpMessage: ''
```

### -EnforceSignatureCheck

Analyze Win32 detection and requirement scripts as if the rule's "Enforce script signature
check" were on: an unsigned script gets an IslSignatureIssue error, which has no fix and is
counted in Remaining. The directive comment "# IntuneScriptLab: EnforceSignatureCheck=true"
does the same for one script, and the settings key EnforceSignatureCheck = $true for a folder.

```yaml
Type: System.Management.Automation.SwitchParameter
DefaultValue: ''
SupportsWildcards: false
Aliases: []
ParameterSets:
- Name: (All)
Position: Named
IsRequired: false
ValueFromPipeline: false
ValueFromPipelineByPropertyName: false
ValueFromRemainingArguments: false
DontShow: false
AcceptedValues: []
HelpMessage: ''
```

### -ExcludeRule

Rule names (wildcards allowed) whose findings are not fixed.
Expand Down
72 changes: 72 additions & 0 deletions en-US/IntuneScriptLab-Help.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4197,6 +4197,22 @@ The command supports
<maml:name>System.String</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="named" aliases="none">
<maml:name>Context</maml:name>
<maml:description />
<command:parameterValue required="true" variableLength="false">string</command:parameterValue>
<dev:type>
<maml:name>System.String</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="named" aliases="none">
<maml:name>Architecture</maml:name>
<maml:description />
<command:parameterValue required="true" variableLength="false">string</command:parameterValue>
<dev:type>
<maml:name>System.String</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="named" aliases="none">
<maml:name>IncludeRule</maml:name>
<maml:description />
Expand All @@ -4213,6 +4229,13 @@ The command supports
<maml:name>System.String[]</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="named" aliases="none">
<maml:name>EnforceSignatureCheck</maml:name>
<maml:description />
<dev:type>
<maml:name>System.Management.Automation.SwitchParameter</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="named" aliases="none">
<maml:name>Settings</maml:name>
<maml:description />
Expand All @@ -4238,6 +4261,20 @@ The command supports
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="Named" aliases="none">
<maml:name>Architecture</maml:name>
<maml:description>
<maml:para>The host the script runs in, passed to the analysis: x86 (portal default for scripts and
remediations), x64 (Win32 detection default) or arm64. Auto (default) infers per script as
Test-IntuneScript does. The findings an architecture decides, System32 against Sysnative among
them, and the fixes and Remaining count that follow from them, are then the ones
Test-IntuneScript gives for the same value.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="true">System.String</command:parameterValue>
<dev:type>
<maml:name>System.String</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="Named" aliases="cf">
<maml:name>Confirm</maml:name>
<maml:description>
Expand All @@ -4247,6 +4284,30 @@ The command supports
<maml:name>System.Management.Automation.SwitchParameter</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="Named" aliases="none">
<maml:name>Context</maml:name>
<maml:description>
<maml:para>System or User, passed to the analysis. Auto (default) uses the directive or the type's portal
default, as Test-IntuneScript does. HKCU: and the profile variables are errors under System and
not under User, so Remaining follows the context the script is deployed in.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="true">System.String</command:parameterValue>
<dev:type>
<maml:name>System.String</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="Named" aliases="none">
<maml:name>EnforceSignatureCheck</maml:name>
<maml:description>
<maml:para>Analyze Win32 detection and requirement scripts as if the rule's "Enforce script signature
check" were on: an unsigned script gets an IslSignatureIssue error, which has no fix and is
counted in Remaining. The directive comment "# IntuneScriptLab: EnforceSignatureCheck=true"
does the same for one script, and the settings key EnforceSignatureCheck = $true for a folder.</maml:para>
</maml:description>
<dev:type>
<maml:name>System.Management.Automation.SwitchParameter</maml:name>
</dev:type>
</command:parameter>
<command:parameter required="false" variableLength="false" globbing="false" pipelineInput="false" position="Named" aliases="none">
<maml:name>ExcludeRule</maml:name>
<maml:description>
Expand Down Expand Up @@ -4365,6 +4426,17 @@ script already did, or changes the file's encoding, never what the script decide
<dev:code />
<dev:remarks />
</command:example>
<command:example>
<maml:title>--------- EXAMPLE 4 ---------</maml:title>
<maml:introduction>
<maml:para>Repair-IntuneScript -Path .\Remediations -Architecture x64 -Context System</maml:para>
<maml:para>&#x80;</maml:para>
<maml:para>Repairs the scripts as deployed to the 64-bit host in system context, so the findings that depend
on either, and the Remaining count, match Test-IntuneScript run with the same options.</maml:para>
</maml:introduction>
<dev:code />
<dev:remarks />
</command:example>
</command:examples>
<command:relatedLinks>
<maml:navigationLink>
Expand Down
Loading