microsoft / microsoft/azure-pipelines-task-lib

VstsTaskSdk's Import-Module -Name Microsoft.PowerShell.Security fails on PS 7.6.5+, breaking every AzurePowerShellV5 pwsh:true task

Open
#1,204 1 comment 0 reactions 0 assignees View on GitHub
Area: TaskLib triage
Dominant language
TypeScript
Stars
458
Forks
282
Avg merge
1d 4h
Merged PRs (30d)
14

Description

## Summary

`AzurePowerShell@5` tasks with `pwsh: true` fail unconditionally on current PowerShell 7 builds (confirmed on 7.6.5, not on 7.6.2) with:

```
WARNING: Proxy creation has been skipped for the following command: 'ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl, Get-AuthenticodeSignature, Get-CmsMessage, Get-Credential, Get-ExecutionPolicy, Get-PfxCertificate, New-FileCatalog, Protect-CmsMessage, Set-Acl, Set-AuthenticodeSignature, Set-ExecutionPolicy, Test-FileCatalog, Unprotect-CmsMessage', because it would shadow an existing local command. Use the AllowClobber parameter if you want to shadow existing local commands.
##[error]Failed to generate proxies for remote module 'Microsoft.PowerShell.Security'. No command proxies have been created, because all of the requested remote commands would shadow existing local commands. Use the AllowClobber parameter if you want to shadow existing local commands.
##[error]PowerShell exited with code '1'.
```

The task fails before the user's own `Inline`/`ScriptPath` content ever runs — the error originates in `VstsTaskSdk.psm1`, which is dot-sourced (via `ImportVstsTaskSdk.ps1`) as the very first thing `AzurePowerShellV5`'s generated script does, before `InitializeAz.ps1` or any pipeline-authored code.

## Root cause

[`VstsTaskSdk.psm1`](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/powershell/VstsTaskSdk/VstsTaskSdk.psm1) contains:

```powershell
# Load the module that contains ConvertTo-SecureString.
if (!(Get-Module -Name Microsoft.PowerShell.Security)) {
Write-Verbose "Importing the module 'Microsoft.PowerShell.Security'."
Import-Module -Name Microsoft.PowerShell.Security 2>&1 |
ForEach-Object {
if (`$_ -is [System.Management.Automation.ErrorRecord]) {
Write-Verbose `$_.Exception.Message
} else {
,`$_
}
}
}
```

On a fresh `-NoProfile -NonInteractive` pwsh session where `Microsoft.PowerShell.Security` isn't already loaded, this plain `Import-Module` call — with no `-UseWindowsPowerShell` and no `-AllowClobber` — ends up routed through Windows PowerShell Compatibility on current PS7 builds, and hits WinCompat's hard-coded no-clobber protection for that module (one of the 7 "core" modules protected since PS 7.1 per [about_Windows_PowerShell_Compatibility](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_windows_powershell_compatibility)). The `if (!(Get-Module ...))` guard means this only fires when the module genuinely isn't pre-loaded yet — which is exactly the state of a freshly-started task process.

## Minimal repro

No Azure auth, no Az module, no pipeline needed — reproduces standalone:

```powershell
pwsh -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "if (!(Get-Module -Name Microsoft.PowerShell.Security)) { Import-Module -Name Microsoft.PowerShell.Security -ErrorAction Stop }; Write-Output 'SUCCEEDED'"
```

- PowerShell **7.6.2**: prints `SUCCEEDED`.
- PowerShell **7.6.5**: throws the exact error above.

### Important nuance found during investigation

This only reproduces on a **real, deployed/specialized Windows instance** — we could not reproduce it on a freshly-provisioned VM that had never been through Windows sysprep/generalize + first-boot specialize (both 7.6.2 and 7.6.5 succeed there). Once deployed from a captured, sysprepped image, both PS7 builds we tested reproduce the clobber depending on version as above. We don't have a root cause for *why* sysprep state matters here, but it's a precondition worth knowing when trying to reproduce this.

## Impact

Any `AzurePowerShell@5` task with `pwsh: true` fails immediately and unconditionally, regardless of the task's own script content, service connection type, or Az module version (`LatestVersion` vs pinned) — because the failure happens in `VstsTaskSdk.psm1`, before `InitializeAz.ps1` (which handles the actual `azurePowerShellVersion` module selection) even runs.

## Workaround

We worked around this by switching the affected tasks from `AzurePowerShell@5` to `AzureCLI@2` (`scriptType: pscore`), bridging the az CLI's own service-connection auth into an Az PowerShell context manually via `Connect-AzAccount -AccessToken ...`. `AzureCLI@2`'s PowerShell invocation ([`ScriptType.ts`](https://github.com/microsoft/azure-pipelines-tasks/blob/master/Tasks/AzureCLIV2/src/ScriptType.ts)) never dot-sources `VstsTaskSdk`, so it isn't exposed to this bug at all. This is a functional workaround, not a fix — ideally `AzurePowerShellV5` shouldn't fail here in the first place.

## Suggested fix

The `Import-Module -Name Microsoft.PowerShell.Security` call in `VstsTaskSdk.psm1` only exists to guarantee `ConvertTo-SecureString` is available (per its own comment). Since `Microsoft.PowerShell.Security` is a native, bundled PS7 module and this failure mode only occurs via a clobber-protection false-positive, either:
- Wrap the call with `-ErrorAction SilentlyContinue` (the module's cmdlets are used elsewhere unconditionally regardless, so a failed proxy-generation attempt for an already-available native module shouldn't be fatal to the whole task), or
- Check for the specific cmdlet (`Get-Command ConvertTo-SecureString -ErrorAction SilentlyContinue`) rather than the module, since PS7 natively provides it without needing this import at all.

## Environment

- Task: `AzurePowerShellV5`
- Agent: Windows, self-hosted Azure DevOps Managed DevOps Pool
- PowerShell: 7.6.5 (repros), 7.6.2 (does not repro)
- `pwsh: true`, `azurePowerShellVersion: LatestVersion`

Contributor guide

Open the contributing guide

Research direction

Start with powershell/VstsTaskSdk/VstsTaskSdk.psm1 and the AzurePowerShellV5 entry flow through ImportVstsTaskSdk.ps1, then run the provided pwsh 7.6.5 minimal reproduction on a specialized Windows instance. Done means AzurePowerShellV5 with pwsh:true gets past SDK initialization without the Microsoft.PowerShell.Security proxy error, while the existing task behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, powershell
Domain
devops
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.