Skip to content

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

Description

@IvoDings1994

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 contains:

# 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). 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:

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) 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions