Skip to content

Import Az.Accounts==5.3.0 fails when using Azure Powershell (Azure DevOps pipeline task) #28495

Closed

Description

@Econ808

Description

We are using an Azure Data Factory npm module to deploy Azure Data Factory templates. This module depends on the Az.Accounts module, however, since version 5.3.0, the azure pipelines are failing with the following error:

==============================================================================
Task : Azure PowerShell
Description : Run a PowerShell script within an Azure environment
Version : 5.260.0
Author : Microsoft Corporation
Help : https://aka.ms/azurepowershelltroubleshooting

AZUREPS_HOST_ENVIRONMENT: ADO/AzurePowerShell@v5_Linux_d-vmss-ne-are087-stoka-dev2-agentscaleset0000N3_app-STOKA_ETL_256967__
Generating script.
Formatted command: . '/agent/_work/1/ArmTemplates/PrePostDeploymentScript.ps1' -ArmTemplate "/agent/_work/1/ArmTemplates/ARMTemplateForFactory.json" -ArmTemplateParameters "/agent/_work/1/ArmTemplates/parameters/adf-template-parameters.json" -ResourceGroupName "d-rg-ne-are087-stoka-dev2-main" -DataFactoryName "d-adf-ne-are087-stoka-dev2-main-01" -predeployment $true -deleteDeployment $false
/usr/bin/pwsh -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command . '/agent/_work/_temp/94b5e615-489d-4667-bc48-b7443afc7dde.ps1'
File saved!
Import-Module -Name /home/AzDevOps/.local/share/powershell/Modules/Az.Accounts/5.3.0/Az.Accounts.psd1 -Global
WARNING: Unable to find type [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer].
Import-Module: Could not load file or assembly 'Microsoft.Identity.Client.Extensions.Msal,
Version=4.65.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae'. The system
cannot find the file specified.

##[error]PowerShell exited with code '1'.
/usr/bin/pwsh -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command . '/agent/_work/_tasks/AzurePowerShell_72a1931b-effb-4d2e-8fd8-f8472a07cb62/5.260.0/RemoveAzContext.ps1'
WARNING: Unable to find type [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer].

Issue script & Debug output

<#
.SYNOPSIS
    Stop/ start triggers during release process (CICD)
.DESCRIPTION
    The script can be used to stop triggers before deployment and restrat them afterward. It stops the trigger only if the trigger is in started state and there is a change in trigger
.PARAMETER ArmTemplate
    Arm template file path
        example: C:\Adf\ArmTemlateOutput\ARMTemplateForFactory.json
.PARAMETER ResourceGroupName
    Name of the resource group where the factory resource is in
.PARAMETER DataFactoryName
    Name of the data factory being deployed
.PARAMETER PreDeployment
    Default: $true
    True: Runs script as pre-deployment so it will stop triggers prior to deployment
    False: Runs script as post-deployment so it will delete the removed resources and start the triggers
.PARAMETER DeleteDeployment
    Default: $false
    Clean-up the deployment labels on the resource group
.PARAMETER ArmTemplateParameters
    Default: $null
    Arm template parameters file path
        example: C:\Adf\ArmTemlateOutput\ARMTemplateParametersForFactory.json
    If not provided, the script will try to find the file with name ARMTemplateParametersForFactory.json in ArmTemplate folder path
.PARAMETER ExplicitStopTriggerList
    Default: @() (An empty array)
    Expliticly stops the triggers form this list if the trigger is in started state even if the trigger payload not changed
        example: "testTrigger", "storageEventsTrigger"
    The script is not very comprehensive in detecting the trigger changes, so this parameter can be used to stop triggers explicitly if required
.PARAMETER MaxJsonDepth
    Default: 100
    The maximum depth the JSON input is allowed to have
#>
param
(
    [parameter(Mandatory = $true)] [String] $ArmTemplate,
    [parameter(Mandatory = $true)] [String] $ResourceGroupName,
    [parameter(Mandatory = $true)] [String] $DataFactoryName,
    [parameter(Mandatory = $false)] [Bool] $PreDeployment = $true,
    [parameter(Mandatory = $false)] [Bool] $DeleteDeployment = $false,
    [parameter(Mandatory = $false)] [String] $ArmTemplateParameters = $null,
    [parameter(Mandatory = $false)] [String[]] $ExplicitStopTriggerList = @(),
    [parameter(Mandatory = $false)] [int] $MaxJsonDepth = 100
)

function Get-PipelineDependency {
    param(
        [System.Object] $activity
    )
    $result = @()
    if ($activity.Pipeline) {
        $result += $activity.Pipeline.ReferenceName
    }
    elseif ($activity.Activities) {
        $activity.Activities | ForEach-Object { $result += Get-PipelineDependency -activity $_ }
    }
    elseif ($activity.ifFalseActivities -or $activity.ifTrueActivities) {
        $activity.ifFalseActivities | Where-Object { $_ -ne $null } | ForEach-Object { $result += Get-PipelineDependency -activity $_ }
        $activity.ifTrueActivities | Where-Object { $_ -ne $null } | ForEach-Object { $result += Get-PipelineDependency -activity $_ }
    }
    elseif ($activity.defaultActivities) {
        $activity.defaultActivities | ForEach-Object { $result += Get-PipelineDependency -activity $_ }
        if ($activity.cases) {
            $activity.cases | ForEach-Object { $_.activities } | ForEach-Object { $result += Get-PipelineDependency -activity $_ }
        }
    }

    return $result
}

function Push-PipelinesToList {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSPipeline]$pipeline,
        [Hashtable] $pipelineNameResourceDict,
        [Hashtable] $visited,
        [System.Collections.Stack] $sortedList
    )
    if ($visited[$pipeline.Name] -eq $true) {
        return;
    }
    $visited[$pipeline.Name] = $true;
    try {
        $pipeline.Activities | ForEach-Object { Get-PipelineDependency -activity $_ -pipelineNameResourceDict $pipelineNameResourceDict }  | ForEach-Object {
            Push-PipelinesToList -pipeline $pipelineNameResourceDict[$_] -pipelineNameResourceDict $pipelineNameResourceDict -visited $visited -sortedList $sortedList
        }
    }
    catch {
        Write-Warning "Encountered error processing pipeline: $($pipeline.Name) in $($DataFactoryName) factory. Please fix the pipeline dependencies." 
        throw
    }
    $sortedList.Push($pipeline)
}

function Get-SortedPipeline {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $pipelines = Get-AzDataFactoryV2Pipeline -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $ppDict = @{}
    $visited = @{}
    $stack = new-object System.Collections.Stack
    $pipelines | ForEach-Object { $ppDict[$_.Name] = $_ }
    $pipelines | ForEach-Object { Push-PipelinesToList -pipeline $_ -pipelineNameResourceDict $ppDict -visited $visited -sortedList $stack }
    $sortedList = new-object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSPipeline]

    while ($stack.Count -gt 0) {
        $sortedList.Add($stack.Pop())
    }
    $sortedList
}

function Push-TriggersToList {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$trigger,
        [Hashtable] $triggerNameResourceDict,
        [Hashtable] $visited,
        [System.Collections.Stack] $sortedList
    )
    if ($visited[$trigger.Name] -eq $true) {
        return;
    }
    $visited[$trigger.Name] = $true;
    if ($trigger.Properties.DependsOn) {
        $trigger.Properties.DependsOn | Where-Object { $_ -and $_.ReferenceTrigger } | ForEach-Object {
            Push-TriggersToList -trigger $triggerNameResourceDict[$_.ReferenceTrigger.ReferenceName] -triggerNameResourceDict $triggerNameResourceDict -visited $visited -sortedList $sortedList
        }
    }
    $sortedList.Push($trigger)
}

function Get-SortedTrigger {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $triggers = Get-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
    $triggerDict = @{}
    $visited = @{}
    $stack = new-object System.Collections.Stack
    $triggers | ForEach-Object { $triggerDict[$_.Name] = $_ }
    $triggers | ForEach-Object { Push-TriggersToList -trigger $_ -triggerNameResourceDict $triggerDict -visited $visited -sortedList $stack }
    $sortedList = new-object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]
    while ($stack.Count -gt 0) {
        $sortedList.Add($stack.Pop())
    }
    $sortedList
}

function Get-SortedLinkedService {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $linkedServices = Get-AzDataFactoryV2LinkedService -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
    $LinkedServiceHasDependencies = @('HDInsightLinkedService', 'HDInsightOnDemandLinkedService', 'AzureBatchLinkedService')
    $Akv = 'AzureKeyVaultLinkedService'
    $HighOrderList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]
    $RegularList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]
    $AkvList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]

    $linkedServices | ForEach-Object {
        if ($_.Properties.GetType().Name -in $LinkedServiceHasDependencies) {
            $HighOrderList.Add($_)
        }
        elseif ($_.Properties.GetType().Name -eq $Akv) {
            $AkvList.Add($_)
        }
        else {
            $RegularList.Add($_)
        }
    }

    $SortedList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]($HighOrderList.Count + $RegularList.Count + $AkvList.Count)
    $SortedList.AddRange($HighOrderList)
    $SortedList.AddRange($RegularList)
    $SortedList.AddRange($AkvList)
    $SortedList
}

function Compare-TriggerPayload {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$triggerDeployed,
        [PSCustomObject]$triggerInTemplate,
        [PSCustomObject]$templateParameters
    )
    try {
        Write-Host "Comparing '$($triggerDeployed.Name)' trigger payload"
        # Parse the trigger json from template to deserialize to trigger object
        $triggerInTemplate.properties.typeProperties | Get-Member -MemberType NoteProperty | ForEach-Object {
            $triggerInTemplate.properties | Add-Member -NotePropertyName $_.Name -NotePropertyValue $triggerInTemplate.properties.typeProperties.$($_.Name) -Force
        }
        $addPropDictionary = New-Object "System.Collections.Generic.Dictionary[System.String, System.Object]"
        $addPropDictionary.Add('typeProperties', $triggerInTemplate.properties.typeProperties)
        $triggerInTemplate.properties | Add-Member -NotePropertyName 'additionalProperties' -NotePropertyValue $addPropDictionary
        $triggerInTemplate.properties.PSObject.Properties.Remove('typeProperties')
        $triggerTemplateJson = ConvertTo-Json -InputObject $triggerInTemplate.properties -Depth $MaxJsonDepth -EscapeHandling Default
        $updatedTemplateJson = Update-TriggerTemplate -templateJson $triggerTemplateJson -templateParameters $templateParameters
        $serializerOptions = New-Object System.Text.Json.JsonSerializerOptions -Property @{ PropertyNameCaseInsensitive = $True }
        $payloadPSObject = $updatedTemplateJson | ConvertFrom-Json -Depth $MaxJsonDepth
        if ($triggerDeployed.Properties.RuntimeState -ne $payloadPSObject.runtimeState) {
            Write-Host "Change detected in '$($triggerDeployed.Name)' trigger payload - runtimeState changed"
            return $True;
        }

        if ($triggerDeployed.Properties.GetType().Name -eq [Microsoft.Azure.Management.DataFactory.Models.ScheduleTrigger].Name) {
            # DayOfWeek needs to have enum value instead of enum strings
            if ($payloadPSObject.recurrence.schedule.weekDays) {
                [System.Array]$payloadPSObject.recurrence.schedule.weekDays = $payloadPSObject.recurrence.schedule.weekDays | ForEach-Object { ([System.DayOfWeek]::$_).value__ }
            }
            if ($payloadPSObject.recurrence.schedule.monthlyOccurrences) {
                $payloadPSObject.recurrence.schedule.monthlyOccurrences | ForEach-Object { $_.day = ([System.DayOfWeek]::$($_.day)).value__ }
            }
            $updatedTemplateJson = ConvertTo-Json -InputObject $payloadPSObject -Depth $MaxJsonDepth

            $triggerPayload = [System.Text.Json.JsonSerializer]::Deserialize($updatedTemplateJson,
                [Microsoft.Azure.Management.DataFactory.Models.ScheduleTrigger],
                $serializerOptions)
            return Compare-ScheduleTrigger -triggerDeployed $triggerDeployed -triggerPayload $triggerPayload
        }
        elseif ($triggerDeployed.Properties.GetType().Name -eq [Microsoft.Azure.Management.DataFactory.Models.TumblingWindowTrigger].Name) {
            $triggerPayload = [System.Text.Json.JsonSerializer]::Deserialize($updatedTemplateJson,
                [Microsoft.Azure.Management.DataFactory.Models.TumblingWindowTrigger],
                $serializerOptions)
            return Compare-TumblingWindowTrigger -triggerDeployed $triggerDeployed -triggerPayload $triggerPayload
        }
        elseif ($triggerDeployed.Properties.GetType().Name -eq [Microsoft.Azure.Management.DataFactory.Models.BlobEventsTrigger].Name) {
            $triggerPayload = [System.Text.Json.JsonSerializer]::Deserialize($updatedTemplateJson,
                [Microsoft.Azure.Management.DataFactory.Models.BlobEventsTrigger],
                $serializerOptions)
            return Compare-BlobEventsTrigger -triggerDeployed $triggerDeployed -triggerPayload $triggerPayload
        }
        elseif ($triggerDeployed.Properties.GetType().Name -eq [Microsoft.Azure.Management.DataFactory.Models.CustomEventsTrigger].Name) {
            $triggerPayload = [System.Text.Json.JsonSerializer]::Deserialize($updatedTemplateJson,
                [Microsoft.Azure.Management.DataFactory.Models.CustomEventsTrigger],
                $serializerOptions)
            return Compare-CustomEventsTrigger -triggerDeployed $triggerDeployed -triggerPayload $triggerPayload
        }

        Write-Host "##[warning] Comparison terminated as trigger type '$($triggerDeployed.Properties.GetType().Name)' not match"
        return $True
    }
    catch {
        Write-Host "##[warning] Unable to compare '$($triggerDeployed.Name)' trigger payload, this is not a failure. You can post the issue to https://github.com/Azure/Azure-DataFactory/issues to check if this is user error or limitation."
        Write-Host "##[warning] $_ from Line: $($_.InvocationInfo.ScriptLineNumber)"
        return $True;
    }
}

function Compare-ScheduleTrigger {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$triggerDeployed,
        [Microsoft.Azure.Management.DataFactory.Models.ScheduleTrigger]$triggerPayload
    )
    # Compare if any common trigger properties changed
    $deployedTriggerProps = $triggerDeployed.Properties
    $descriptionChanges = Compare-Object -ReferenceObject $deployedTriggerProps  -DifferenceObject $triggerPayload -Property Description
    $annotationChanges = Compare-Object -ReferenceObject $deployedtriggerProps.Annotations -DifferenceObject $triggerPayload.Annotations

    # Compare if the recurrence changed
    $recurrencechanges = Compare-Object -ReferenceObject $deployedTriggerProps.Recurrence -DifferenceObject $triggerPayload.Recurrence `
        -Property Frequency, Interval, StartTime, EndTime, TimeZone

    # Compare if the schedule changed
    $scheduleChanged = $True;
    if ($null -ne $deployedTriggerProps.Recurrence.Schedule -and $null -ne $triggerPayload.Recurrence.Schedule) {
        $changes = Compare-Object -ReferenceObject $deployedTriggerProps.Recurrence.Schedule -DifferenceObject $triggerPayload.Recurrence.Schedule `
            -Property Minutes, Hours, WeekDays, MonthDays

        # Compare monthly occurrences
        if ($null -eq $changes) {
            $scheduleChanged = $True;
            if ($null -ne $deployedTriggerProps.Recurrence.Schedule.MonthlyOccurrences -and $null -ne $triggerPayload.Recurrence.Schedule.MonthlyOccurrences) {
                $changes =Compare-Object -ReferenceObject $deployedTriggerProps.Recurrence.Schedule.MonthlyOccurrences -DifferenceObject $triggerPayload.Recurrence.Schedule.MonthlyOccurrences `
                    -Property Day, Occurrence
            } elseif ($null -eq $deployedTriggerProps.Recurrence.Schedule.MonthlyOccurrences -and $null -eq $triggerPayload.Recurrence.Schedule.MonthlyOccurrences) {
                $scheduleChanged = $False;
            }
        }

        $scheduleChanged = $null -ne $changes
    } elseif ($null -eq $deployedTriggerProps.Recurrence.Schedule -and $null -eq $triggerPayload.Recurrence.Schedule) {
        $scheduleChanged = $False;
    }

    # Compare to check if there is any change in referenced pipeline
    $pipelineRefChanged = Compare-TriggerPipelineReference -tprDeployed $deployedTriggerProps.Pipelines -tprPayload $triggerPayload.Pipelines

    # Temporary workaround for Get-AzDataFactoryV2Trigger returning incorrect datetime notation
    # https://github.com/Azure/azure-powershell/issues/20474 issue, thanks to gvdmaaden for this workaround
    Update-TriggerTimeFormat -deployedAdditionalProps $deployedTriggerProps.AdditionalProperties

    # Compare additional properties (unmatched properties stay here)
    $additionalPropsChanged = Compare-TriggerAdditionalProperty -deployedAdditionalProps $deployedTriggerProps.AdditionalProperties `
        -payloadAdditionalProps $triggerPayload.AdditionalProperties

    if (($null -ne $descriptionChanges) -or ($null -ne $annotationChanges) -or ($null -ne $recurrencechanges) -or `
            $scheduleChanged -or $pipelineRefChanged -or $additionalPropsChanged) {
        Write-Host "Change detected in '$($triggerDeployed.Name)' trigger payload - descriptionChanges=$($descriptionChanges.Length), annotationChanges=$($annotationChanges.Length), recurrencechanges=$($recurrencechanges.Length), scheduleChanged=$scheduleChanged, pipelineRefChanged=$pipelineRefChanged, additionalPropsChanged=$additionalPropsChanged"
        return $True
    }

    Write-Host "No change detected in '$($triggerDeployed.Name)' trigger payload"
    return $False;
}

function Compare-TumblingWindowTrigger {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$triggerDeployed,
        [Microsoft.Azure.Management.DataFactory.Models.TumblingWindowTrigger]$triggerPayload
    )
    # Compare if any of common tumbling window trigger properties changed
    $propertyChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties -DifferenceObject $triggerPayload `
        -Property Frequency, Interval, StartTime, EndTime, Delay, MaxConcurrency, Description
    $annotationChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties.Annotations -DifferenceObject $triggerPayload.Annotations
    $retryPolicyChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties.RetryPolicy -DifferenceObject $triggerPayload.RetryPolicy `
        -Property Count, IntervalInSeconds

    # Compare to check if there is any change in referenced pipeline
    $tprDeployed = New-Object System.Collections.Generic.List[Microsoft.Azure.Management.DataFactory.Models.TriggerPipelineReference]
    $tprDeployed.Add($triggerDeployed.Properties.Pipeline)
    $tprPayload = New-Object System.Collections.Generic.List[Microsoft.Azure.Management.DataFactory.Models.TriggerPipelineReference]
    $tprPayload.Add($triggerPayload.Pipeline)
    $pipelineRefChanged = Compare-TriggerPipelineReference -tprDeployed $tprDeployed -tprPayload $tprPayload

    # Compare additional properties (unmatched properties stay here ex: DependsOn)
    $additionalPropsChanged = Compare-TriggerAdditionalProperty -deployedAdditionalProps $triggerDeployed.Properties.AdditionalProperties `
        -payloadAdditionalProps $triggerPayload.AdditionalProperties

    if (($null -ne $propertyChanges) -or ($null -ne $annotationChanges) -or ($null -ne $retryPolicyChanges) -or `
        $pipelineRefChanged -or $additionalPropsChanged) {
        Write-Host "Change detected in '$($triggerDeployed.Name)' trigger payload - propertyChanges=$($propertyChanges.Length), annotationChanges=$($annotationChanges.Length), retryPolicyChanges=$($retryPolicyChanges.Length), pipelineRefChanged=$pipelineRefChanged, additionalPropsChanged=$additionalPropsChanged"
        return $True
    }

    Write-Host "No change detected in '$($triggerDeployed.Name)' trigger payload"
    return $False;
}

function Compare-BlobEventsTrigger {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$triggerDeployed,
        [Microsoft.Azure.Management.DataFactory.Models.BlobEventsTrigger]$triggerPayload
    )
    $propertyChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties -DifferenceObject $triggerPayload `
        -Property BlobPathBeginsWith, BlobPathEndsWith, IgnoreEmptyBlobs, Events, Scope, Description
    $annotationChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties.Annotations -DifferenceObject $triggerPayload.Annotations

    # Compare to check if there is any change in referenced pipeline
    $pipelineRefChanged = Compare-TriggerPipelineReference -tprDeployed $triggerDeployed.Properties.Pipelines -tprPayload $triggerPayload.Pipelines

    # Compare additional properties (unmatched properties stay here - ex: advancedFilters)
    $additionalPropsChanged = Compare-TriggerAdditionalProperty -deployedAdditionalProps $triggerDeployed.Properties.AdditionalProperties `
        -payloadAdditionalProps $triggerPayload.AdditionalProperties

    if (($null -ne $propertyChanges) -or ($null -ne $annotationChanges) -or $pipelineRefChanged -or $additionalPropsChanged) {
        Write-Host "Change detected in '$($triggerDeployed.Name)' trigger payload - propertyChanges=$($propertyChanges.Length), annotationChanges=$($annotationChanges.Length), pipelineRefChanged=$pipelineRefChanged, additionalPropsChanged=$additionalPropsChanged"
        return $True
    }

    Write-Host "No change detected in '$($triggerDeployed.Name)' trigger payload"
    return $False;
}

function Compare-CustomEventsTrigger {
    param(
        [Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$triggerDeployed,
        [Microsoft.Azure.Management.DataFactory.Models.CustomEventsTrigger]$triggerPayload
    )
    # Compare common and event properties
    $propertyChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties -DifferenceObject $triggerPayload `
        -Property SubjectBeginsWith, SubjectEndsWith, Scope, Description
    $eventChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties.Events -DifferenceObject $triggerPayload.Events
    $annotationChanges = Compare-Object -ReferenceObject $triggerDeployed.Properties.Annotations -DifferenceObject $triggerPayload.Annotations


    # Compare to check if there is any change in referenced pipeline
    $pipelineRefChanged = Compare-TriggerPipelineReference -tprDeployed $triggerDeployed.Properties.Pipelines -tprPayload $triggerPayload.Pipelines

    # Compare additional properties (unmatched properties stay here - ex: advancedFilters)
    $additionalPropsChanged = Compare-TriggerAdditionalProperty -deployedAdditionalProps $triggerDeployed.Properties.AdditionalProperties `
        -payloadAdditionalProps $triggerPayload.AdditionalProperties

    if (($null -ne $propertyChanges) -or ($null -ne $eventChanges) -or ($null -ne $annotationChanges) -or `
        $pipelineRefChanged -or $additionalPropsChanged) {
        Write-Host "Change detected in '$($triggerDeployed.Name)' trigger payload - propertyChanges=$($propertyChanges.Length), eventChanges=$($eventChanges.Length), annotationChanges=$($annotationChanges.Length), pipelineRefChanged=$pipelineRefChanged, additionalPropsChanged=$additionalPropsChanged"
        return $True
    }

    Write-Host "No change detected in '$($triggerDeployed.Name)' trigger payload"
    return $False;
}

function Compare-TriggerPipelineReference {
    param(
        [System.Collections.Generic.IList[Microsoft.Azure.Management.DataFactory.Models.TriggerPipelineReference]]$tprDeployed,
        [System.Collections.Generic.IList[Microsoft.Azure.Management.DataFactory.Models.TriggerPipelineReference]]$tprPayload
    )
    # Compare to check if there is any change in referenced pipeline
    $pipelineRefchanged = $True;
    if ($null -ne $tprDeployed.PipelineReference -and $null -ne $tprPayload.PipelineReference) {
        $changes = Compare-Object -ReferenceObject $tprDeployed.PipelineReference -DifferenceObject $tprPayload.PipelineReference `
            -Property Name, ReferenceName
        $pipelineRefchanged = $changes.Length -gt 0
    }
    elseif ($null -eq $tprDeployed.PipelineReference -and $null -eq $tprPayload.PipelineReference) {
        $pipelineRefchanged = $False;
    }

    # If no change in pipeline reference, compare to check if there is any change in pipeline parameters
    $paramsChanged = $True
    if (!$pipelineRefchanged -and $tprPayload.Count -gt 0) {
        $paramsChanged = $False
        for ($counter = 0; $counter -lt $tprPayload.count; $counter++) {
            $pipelineReferenceName = $tprPayload[$counter].PipelineReference.ReferenceName
            $payloadPipelineRef = $tprPayload | Where-Object { $_.PipelineReference.ReferenceName -eq $pipelineReferenceName }
            $deployedPipelineRef = $tprDeployed | Where-Object { $_.PipelineReference.ReferenceName -eq $pipelineReferenceName }
            if ($deployedPipelineRef.Parameters.Keys.Count -eq $payloadPipelineRef.Parameters.Keys.Count) {
                foreach ($key in $deployedPipelineRef.Parameters.Keys) {
                    $deployedValue = $null
                    $payloadValue = $null
                    if (!$payloadPipelineRef.Parameters.TryGetValue($key, [ref]$payloadValue) -or
                        !$deployedPipelineRef.Parameters.TryGetValue($key, [ref]$deployedValue) ) {
                        $paramsChanged = $True
                        break
                    }
                    else {
                        if ($deployedValue.GetType().Name -in @("JObject", "JArray")) {
                            if (($deployedValue.ToString() | ConvertFrom-Json).type -eq "SecureString") {
                                $paramsChanged = $True
                                Write-Host "##[warning] SecureString parameter is always treated as a change"
                                break
                            } else {
                                $paramValueChanges = Compare-Object -ReferenceObject ($deployedValue.ToString() | ConvertFrom-Json) -DifferenceObject ($payloadValue.ToString() | ConvertFrom-Json)
                            }
                        } elseif ($deployedValue.GetType().Name -eq "Boolean") {
                            $paramValueChanges = Compare-Object -ReferenceObject ($deployedValue.ToString().ToLower() | ConvertFrom-Json) -DifferenceObject ($payloadValue.ToString().ToLower() | ConvertFrom-Json)
                        } else {
                            $paramValueChanges = Compare-Object -ReferenceObject $deployedValue -DifferenceObject $payloadValue
                        }
                        if ($paramValueChanges.Length -gt 0) {
                            $paramsChanged = $True
                            break
                        }
                    }
                }
            }
            else {
                $paramsChanged = $True
                break;
            }
        }
    }

    return $pipelineRefchanged -or $paramsChanged
}

function Compare-TriggerAdditionalProperty {
    param(
        [System.Collections.Generic.Dictionary[String, System.Object]]$deployedAdditionalProps,
        [System.Collections.Generic.Dictionary[String, System.Object]]$payloadAdditionalProps
    )
    $additionalPropchanged = $True;
    if ($null -ne $deployedAdditionalProps -and $null -ne $payloadAdditionalProps) {
        $changes = Compare-Object -ReferenceObject $deployedAdditionalProps -DifferenceObject $payloadAdditionalProps `
            -Property Keys
        $additionalPropchanged = $null -ne $changes
        if (-not $additionalPropchanged) {
            foreach ($key in $deployedAdditionalProps.Keys) {
                $deployedValue = $null; $payloadValue = $null;
                if (!$payloadAdditionalProps.TryGetValue($key, [ref]$payloadValue) -or
                    !$deployedAdditionalProps.TryGetValue($key, [ref]$deployedValue)) {
                    $additionalPropchanged = $True
                    break
                }
                else {
                    $payloadJObect = [Newtonsoft.Json.Linq.JObject]::Parse($payloadValue)
                    $additionalPropValueChanges = Compare-Object -ReferenceObject $deployedValue -DifferenceObject $payloadJObect
                    if ($null -ne $additionalPropValueChanges) {
                        $additionalPropchanged = $True
                        break
                    }
                }
            }
        }
    }
    elseif ($null -eq $deployedAdditionalProps -and $null -eq $payloadAdditionalProps) {
        $additionalPropchanged = $False;
    }

    return $additionalPropchanged
}

function Update-TriggerTimeFormat {
    param(
        [System.Collections.Generic.Dictionary[String, System.Object]]$deployedAdditionalProps
    )
    foreach ($key in $deployedAdditionalProps.Keys) {
        $deployedValue = $null;
        if ($deployedAdditionalProps.TryGetValue($key, [ref]$deployedValue) -and $deployedValue["recurrence"]["timeZone"] -ne "UTC")
        {
            if ($null -ne $deployedValue["recurrence"]["startTime"]) {
                $startTimeString = $deployedValue["recurrence"]["startTime"].ToString("yyyy-MM-ddTHH:mm:ss");
                $startDateTime = Get-Date -Date $startTimeString;
                $deployedValue["recurrence"]["startTime"] = New-Object DateTime 2000, 1, 1, 1, 0, 0, ([DateTimeKind]::Unspecified)
                $deployedValue["recurrence"]["startTime"] = New-Object DateTime $startDateTime.Year, $startDateTime.Month, $startDateTime.Day, $startDateTime.Hour, $startDateTime.Minute, $startDateTime.Second, ([DateTimeKind]::Unspecified)
            }

            if ($null -ne $deployedValue["recurrence"]["endTime"]) {
                $endTimeString = $deployedValue["recurrence"]["endTime"].ToString("yyyy-MM-ddTHH:mm:ss");
                $endDateTime = Get-Date -Date $endTimeString;
                $deployedValue["recurrence"]["endTime"] = New-Object DateTime 2000, 1, 1, 1, 0, 0, ([DateTimeKind]::Unspecified)
                $deployedValue["recurrence"]["endTime"] = New-Object DateTime $endDateTime.Year, $endDateTime.Month, $endDateTime.Day, $endDateTime.Hour, $endDateTime.Minute, $endDateTime.Second, ([DateTimeKind]::Unspecified)
            }
        }
    }
}

function Update-TriggerTemplate {
    param(
        [string]$templateJson,
        [PSCustomObject]$templateParameters
    )
    $parameterMatches = [System.Text.RegularExpressions.Regex]::Matches($templateJson, '\[parameters\([^)]*\)\]')
    foreach ($parameterMatch in $parameterMatches) {
        [string]$parameterName = $parameterMatch.Value.Substring(13, $parameterMatch.Value.Length - 16)
        if ($null -ne $templateParameters.$($parameterName) && ($PreDeployment || $parameterName.EndsWith('runtimeState'))) {
            $parameterType = $null -ne $templateParameters.$($parameterName).value ? $templateParameters.$($parameterName).value.GetType().Name : $null
            if ($parameterType -eq 'Object[]' -or $parameterType -eq 'PSCustomObject') {
                $parameterValue = ConvertTo-Json $templateParameters.$($parameterName).value -Depth $MaxJsonDepth -EscapeHandling Default
                $templateJson = $templateJson -replace [System.Text.RegularExpressions.Regex]::Escape("`"$($parameterMatch.Value)`""), $parameterValue
            } elseif ($parameterType -eq 'Int64') {
                $templateJson = $templateJson -replace [System.Text.RegularExpressions.Regex]::Escape("`"$($parameterMatch.Value)`""), $templateParameters.$($parameterName).value
            } elseif ($parameterType -eq 'Boolean') {
                $templateJson = $templateJson -replace [System.Text.RegularExpressions.Regex]::Escape("`"$($parameterMatch.Value)`""), $templateParameters.$($parameterName).value.ToString().ToLower()
            } else {
                $templateJson = $templateJson -replace [System.Text.RegularExpressions.Regex]::Escape($parameterMatch.Value), $templateParameters.$($parameterName).value
            }
        }
    }

    return $templateJson
}

try {
    # Show warning message that PowerShell Core or PowerShell version > 7.0 is required
    $PSCompatible = $True
    if ($PSVersionTable.PSEdition -ne 'Core' -and ([System.Version]$PSVersionTable.PSVersion -lt [System.Version]"7.0.0")) {
        $PSCompatible = $False
        Write-Host "##[warning] The script is not compatible with your current PowerShell version $($PSVersionTable.PSVersion). Use either PowerShell Core or at least PS version 7.0, otherwise the script may fail to compare the trigger payload and always stop/start the trigger(s)"
    }

    $templateJson = Get-Content $ArmTemplate | ConvertFrom-Json
    $resources = $templateJson.resources

    if (-not $ArmTemplateParameters) {
        $ArmTemplateParameters = Join-Path -Path (Split-Path $ArmTemplate -Parent) -ChildPath 'ArmTemplateParametersForFactory.json'
        Write-Host "##[warning] Arm-template parameter file path not specified, the script will look for the file in arm-template file path."
    }

    $templateParameters = $null
    if (Test-Path -Path $ArmTemplateParameters) {
        $templateParametersJson = Get-Content $ArmTemplateParameters | ConvertFrom-Json
        $templateParameters = $templateParametersJson.parameters
    } else {
        Write-Host "##[warning] The script couldn't find the arm-tempalte parameter file in the arm-template file path, the trigger comparison won't work for parameterized properties. Please pass the arm-template parameter file path to ArmTemplateParameters script argument."
    }

    #Triggers
    Write-Host "Getting triggers"
    $triggersInTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/triggers" }
    $triggerNamesInTemplate = $triggersInTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }

    $triggersDeployed = Get-SortedTrigger -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName

    if ($PreDeployment -eq $true) {
        #Stop trigger only if there is change in payload
        $triggersToStop = $triggersDeployed | Where-Object { $_.Name -in $triggerNamesInTemplate -and $_.RuntimeState -ne 'Stopped' } `
        | Where-Object {
            $triggerName = $_.Name;
            $triggerInTemplate = $triggersInTemplate | Where-Object { $_.name.Substring(37, $_.name.Length - 40) -eq $triggerName };
            Compare-TriggerPayload -triggerDeployed $_ -triggerInTemplate $triggerInTemplate -templateParameters $templateParameters
        } `
        | ForEach-Object {
            New-Object PSObject -Property @{
                Name        = $_.Name
                TriggerType = $_.Properties.GetType().Name
            }
        }

        Write-Host "Stopping $($triggersToStop.Count) triggers  `n"
        $triggersToStop | ForEach-Object {
            if ($_.TriggerType -eq 'BlobEventsTrigger') {
                Write-Host "Unsubscribing $($_.Name) from events"
                $status = Remove-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                while ($status.Status -ne 'Disabled') {
                    Start-Sleep -s 15
                    $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                }
            }
            Write-Host "Stopping trigger $($_.Name)"
            Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
        }

        $explicitTriggersToStop = $triggersDeployed | Where-Object { $_.Name -in $triggerNamesInTemplate -and $_.RuntimeState -ne 'Stopped' } `
        | Where-Object { $_.Name -in $ExplicitStopTriggerList } `
        | ForEach-Object {
            New-Object PSObject -Property @{
                Name        = $_.Name
                TriggerType = $_.Properties.GetType().Name
            }
        }

        if ($explicitTriggersToStop -and $explicitTriggersToStop.Count -gt 0) {
            Write-Host "Stopping $($explicitTriggersToStop.Count) triggers from explicit stop-trigger list `n"
            $explicitTriggersToStop | ForEach-Object {
                if ($_.TriggerType -eq 'BlobEventsTrigger') {
                    Write-Host "Unsubscribing $($_.Name) from events"
                    $status = Remove-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                    while ($status.Status -ne 'Disabled') {
                        Start-Sleep -s 15
                        $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                    }
                }
                Write-Host "Stopping trigger $($_.Name)"
                Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
            }
        } elseif ($ExplicitStopTriggerList -and $ExplicitStopTriggerList.Count -gt 0) {
            Write-Host "No matching trigger (in started state) to stop from explicit stop-trigger list"
        }
    }
    else {
        #Deleted resources
        #pipelines
        Write-Host "Getting pipelines"
        $pipelinesADF = Get-SortedPipeline -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
        $pipelinesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/pipelines" }
        $pipelinesNames = $pipelinesTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }
        $deletedpipelines = $pipelinesADF | Where-Object { $pipelinesNames -notcontains $_.Name }
        #dataflows
        $dataflowsADF = Get-AzDataFactoryV2DataFlow -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
        $dataflowsTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/dataflows" }
        $dataflowsNames = $dataflowsTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }
        $deleteddataflow = $dataflowsADF | Where-Object { $dataflowsNames -notcontains $_.Name }
        #datasets
        Write-Host "Getting datasets"
        $datasetsADF = Get-AzDataFactoryV2Dataset -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
        $datasetsTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/datasets" }
        $datasetsNames = $datasetsTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }
        $deleteddataset = $datasetsADF | Where-Object { $datasetsNames -notcontains $_.Name }
        #linkedservices
        Write-Host "Getting linked services"
        $linkedservicesADF = Get-SortedLinkedService -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
        $linkedservicesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/linkedservices" }
        $linkedservicesNames = $linkedservicesTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }
        $deletedlinkedservices = $linkedservicesADF | Where-Object { $linkedservicesNames -notcontains $_.Name }
        #Integrationruntimes
        Write-Host "Getting integration runtimes"
        $integrationruntimesADF = Get-AzDataFactoryV2IntegrationRuntime -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
        $integrationruntimesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/integrationruntimes" }
        $integrationruntimesNames = $integrationruntimesTemplate | ForEach-Object { $_.name.Substring(37, $_.name.Length - 40) }
        $deletedintegrationruntimes = $integrationruntimesADF | Where-Object { $integrationruntimesNames -notcontains $_.Name }

        #Delete resources
        Write-Host "Deleting triggers"
        $triggersToDelete = $triggersDeployed | Where-Object { $triggerNamesInTemplate -notcontains $_.Name } | ForEach-Object {
            New-Object PSObject -Property @{
                Name        = $_.Name
                TriggerType = $_.Properties.GetType().Name
            }
        }
        $triggersToDelete | ForEach-Object {
            Write-Host "Deleting trigger $($_.Name)"
            $trig = Get-AzDataFactoryV2Trigger -name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
            if ($trig.RuntimeState -eq 'Started') {
                if ($_.TriggerType -eq 'BlobEventsTrigger') {
                    Write-Host "Unsubscribing trigger $($_.Name) from events"
                    $status = Remove-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                    while ($status.Status -ne 'Disabled') {
                        Start-Sleep -s 15
                        $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                    }
                }
                Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
            }
            Remove-AzDataFactoryV2Trigger -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }
        Write-Host "Deleting pipelines"
        $deletedpipelines | ForEach-Object {
            Write-Host "Deleting pipeline $($_.Name)"
            Remove-AzDataFactoryV2Pipeline -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }
        Write-Host "Deleting dataflows"
        $deleteddataflow | ForEach-Object {
            Write-Host "Deleting dataflow $($_.Name)"
            Remove-AzDataFactoryV2DataFlow -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }
        Write-Host "Deleting datasets"
        $deleteddataset | ForEach-Object {
            Write-Host "Deleting dataset $($_.Name)"
            Remove-AzDataFactoryV2Dataset -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }
        Write-Host "Deleting linked services"
        $deletedlinkedservices | ForEach-Object {
            Write-Host "Deleting Linked Service $($_.Name)"
            Remove-AzDataFactoryV2LinkedService -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }
        Write-Host "Deleting integration runtimes"
        $deletedintegrationruntimes | ForEach-Object {
            Write-Host "Deleting integration runtime $($_.Name)"
            Remove-AzDataFactoryV2IntegrationRuntime -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force
        }

        if ($DeleteDeployment -eq $true) {
            Write-Host "Deleting ARM deployment ... under resource group: $ResourceGroupName"
            $deployments = Get-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName
            $deploymentsToConsider = $deployments | Where-Object { $_.DeploymentName -like "ArmTemplate_master*" -or $_.DeploymentName -like "ArmTemplateForFactory*" } | Sort-Object -Property Timestamp -Descending
            $deploymentName = $deploymentsToConsider[0].DeploymentName

            Write-Host "Deployment to be deleted: $deploymentName"
            $deploymentOperations = Get-AzResourceGroupDeploymentOperation -DeploymentName $deploymentName -ResourceGroupName $ResourceGroupName
            $deploymentsToDelete = $deploymentOperations | Where-Object { $_.properties.targetResource.id -like "*Microsoft.Resources/deployments*" }

            $deploymentsToDelete | ForEach-Object {
                Write-Host "Deleting inner deployment: $($_.properties.targetResource.id)"
                Remove-AzResourceGroupDeployment -Id $_.properties.targetResource.id
            }
            Write-Host "Deleting deployment: $deploymentName"
            Remove-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName -Name $deploymentName
        }

        #Start active triggers - after cleanup efforts
        $triggersRunning = $triggersDeployed | Where-Object { $_.RuntimeState -eq 'Started' } | ForEach-Object { $_.Name }

        $updatedTriggersInTemplate = $triggersInTemplate
        if ($PSCompatible) {
            try {
                $updatedTriggersInTemplate = $triggersInTemplate | ForEach-Object {
                    $triggerJson = ConvertTo-Json -InputObject $_ -Depth $MaxJsonDepth -EscapeHandling Default
                    Update-TriggerTemplate -templateJson $triggerJson -templateParameters $templateParameters
                } | ConvertFrom-Json -Depth $MaxJsonDepth
            } catch {
                Write-Host "##[warning] Unable to update the parameterized properties in trigger template. The script may fail to start triggers if using parameterized runtimeState."
                $updatedTriggersInTemplate = $triggersInTemplate
            }
        }

        $triggersToStart = $updatedTriggersInTemplate | Where-Object { $_.properties.runtimeState -eq 'Started' -and $_.name.Substring(37, $_.name.Length - 40) -notin $triggersRunning } `
        | Where-Object { $_.properties.pipelines.Count -gt 0 -or $_.properties.pipeline.pipelineReference -ne $null } | ForEach-Object {
            New-Object PSObject -Property @{
                Name        = $_.name.Substring(37, $_.name.Length - 40)
                TriggerType = $_.Properties.type
            }
        }

        if ($triggersToStart.Count -gt 0) {
            Write-Host "Starting $($triggersToStart.Count) triggers"
        }

        $triggersToStart | ForEach-Object {
            if ($_.TriggerType -eq 'BlobEventsTrigger') {
                Write-Host "Subscribing $($_.Name) to events"
                $status = Add-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                while ($status.Status -ne 'Enabled') {
                    Start-Sleep -s 15
                    $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                }
            }
            Write-Host "Starting trigger $($_.Name)"
            Start-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
        }
    }
} catch {
    Write-Host "##[error] $_ from Line: $($_.InvocationInfo.ScriptLineNumber)"
    throw
}

Environment data

Task         : Azure PowerShell
Description  : Run a PowerShell script within an Azure environment
Version      : 5.260.0
Author       : Microsoft Corporation
Help         : https://aka.ms/azurepowershelltroubleshooting

Module versions

Az.Accounts 5.3.0

Error output

Activity

  1. added
    bugThis issue requires a change to an existing behavior in the product in order to be resolved.
    needs-triageThis is a new issue that needs to be triaged to the appropriate team.
    on Sep 2, 2025
  2. added
    needs-triageThis is a new issue that needs to be triaged to the appropriate team.
    and removed
    needs-triageThis is a new issue that needs to be triaged to the appropriate team.
    on Sep 2, 2025
  3. isra-fel commented on Sep 3, 2025

    @isra-fel
    Member

    Thanks for reporting! Could you double check the followings:

  4. JvDrunen commented on Sep 3, 2025

    @JvDrunen

    Same here on Self hosted agents (with powershel 7.5.2)
    `WARNING: Unable to find type [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer].
    ##[error] Unable to load module "Az.Accounts"
    | Could not load file or assembly 'Microsoft.Identity.Client.Extensions.Msal, Version=4.65.0.0, Culture=neutral,
    | PublicKeyToken=0a613f4dd989e8ae'. The system cannot find the file specified.

    `

    worked fine with az.accounts 5.2.0

  5. Econ808 commented on Sep 3, 2025

    @Econ808
    Author

    Name Value


    PSVersion 7.3.6
    PSEdition Core
    GitCommitId 7.3.6
    OS Linux 6.8.0-1031-azure #36~22.04.1-Ubuntu SMP T…
    Platform Unix
    PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
    PSRemotingProtocolVersion 2.3
    SerializationVersion 1.1.0.1
    WSManStackVersion 3.0

  6. mikeelindsay commented on Sep 3, 2025

    @mikeelindsay

    upgrade to powershell 7.5, it appears to have fixed this issue for me on our build agents. there was some change on az.accounts that switched from using .net 7 to .net 8 in a recent changelog

    Tobias Ersted (@Econ808)

  7. PawelHaracz commented on Sep 3, 2025

    @PawelHaracz

    Looks that version AZ 14.4.0 broke azure powershell task. I have the same issue and I cannot find any workaround, all my pieplines are failing after the upgrading of AZ module. the most important is this warning: Unable to find type [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer].

  8. isra-fel commented on Sep 4, 2025

    @isra-fel
    Member

    As Michael Lindsay (@mikeelindsay) suggested, this is indeed related to a recent update of target framework from net6 to net8 in one assembly of Az.Accounts module - Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.dll.

    The idea behind this change is that .net 6 has gone end of support late 2024, and it's unsafe to not upgrade.
    Corresponding to the end of support of .net 6, older versions of PowerShell are also deprecated. Check out PowerShell end-of-support dates for details. As of today, the supported versions of PowerShell are 7.4 and 7.5.

    Root cause and troubleshooting

    According to the above feedback, the most likely reason that one might hit this issue in Azure DevOps pipelines is that the latest Azure PowerShell is running on a self-hosted agent with an unsupported version of PowerShell installed. To make sure of this, try adding a powershell task and run $PSVersionTable. If you find out PSVersion is 7.3.*, 7.2.* or even older, then that's the problem.

    Mitigation

    There are two ways to mitigate this:

    1. (Recommended) update PowerShell to a supported version. For now, either 7.4 (LTS) or 7.5 works.
    2. Pin to a previous specific version of Az and Az.Accounts as a workaround before option 1 is applied. Here's a guide for that: https://gist.github.com/isra-fel/2451dc54260ff7e3021e552054a2a42c
  9. Econ808 commented on Sep 4, 2025

    @Econ808
    Author

    The following implementation has fixed our issues for now until a more recent version of Powershell is available to my team. Most important part is pinning version and forcing import of said version (Simply installing Az.Accounts==5.2.0 did not work as the other dependencies ended up installing version 5.3.0 as well):

    requirements.txt:

        Microsoft.Graph
        ImportExcel
        Az.Accounts==5.2.0
        Az.DataFactory
        Az.Resources
    

    Install-Dependencies.ps1:

        param (
            [Parameter(Mandatory=$true)]
            [string[]]$requirementsFile
        )
        
        # Set up PowerShell repository
        Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
        
        # Read modules
        $modules = Get-Content $requirementsFile
        
        # Install each module
        foreach ($moduleEntry in $modules) {
            # Skip empty lines and comments
            if ([string]::IsNullOrWhiteSpace($moduleEntry) -or $moduleEntry.StartsWith('#')) {
                continue
            }
            
            # Parse module name and version
            if ($moduleEntry.Contains('==')) {
                $parts = $moduleEntry.Split('==')
                $moduleName = $parts[0].Trim()
                $moduleVersion = $parts[1].Trim()
                
                Write-Host "Installing $moduleName version $moduleVersion..." -ForegroundColor Green
                Install-Module -Name $moduleName -RequiredVersion $moduleVersion -Verbose -ErrorAction Stop -Scope CurrentUser -Force
            } else {
                $moduleName = $moduleEntry.Trim()
                Write-Host "Installing $moduleName (latest version)..." -ForegroundColor Green
                Install-Module -Name $moduleName -Verbose -ErrorAction Stop -Scope CurrentUser -Force
            }
        }
    

    Azure pipeline tasks:

      - task: PowerShell@2
                  displayName: "Install Dependencies"
                  inputs:
                    filePath: "${{parameters.artifactDir}}/PSScripts/Install-Dependencies.ps1"
                    arguments: "-requirementsFile ${{parameters.artifactDir}}/PSScripts/requirements.txt"
                    pwsh: true
    
       - task: AzureCLI@2
                  displayName: Stop ADF Triggers
                  inputs:
                    azureSubscription: "${{parameters.azureSubscription}}"
                    scriptType: 'bash'
                    scriptLocation: 'inlineScript'
                    inlineScript: |
                      # Use pwsh to run PowerShell script with Azure CLI authentication
                      pwsh -Command "
                      # Import required modules
                      Write-Host 'PowerShell Version:'
                      \$PSVersionTable
                      Import-Module Az.Accounts -RequiredVersion 5.2.0 -Force -Global
                      Import-Module Az.DataFactory -Force
                      Import-Module Az.Resources -Force
                      
                      # Get Azure CLI access token and authenticate PowerShell
                      Write-Host 'Getting Azure CLI access token...'
                      \$tokenInfo = az account get-access-token | ConvertFrom-Json
                      \$accountInfo = az account show | ConvertFrom-Json
                      
                      # Connect using the access token
                      Write-Host 'Connecting to Azure with access token...'
                      Connect-AzAccount -AccessToken \$tokenInfo.accessToken -SubscriptionId \$accountInfo.id -TenantId \$accountInfo.tenantId -AccountId \$accountInfo.user.name
                      
                      # Execute the deployment script
                      & '${{parameters.artifactDir}}/PrePostDeploymentScript.ps1' -ArmTemplate '${{parameters.artifactDir}}/ARMTemplateForFactory.json' -ArmTemplateParameters '${{parameters.artifactDir}}/parameters/adf-template-parameters.json' -ResourceGroupName '$(dataFactoryResourceGroupname)' -DataFactoryName '$(dataFactoryName)' -predeployment \$true -deleteDeployment \$false
                      "
    
  10. belav commented on Sep 8, 2025

    @belav

    Mitigation

    There are two ways to mitigate this:

    1. (Recommended) update PowerShell to a supported version. For now, either 7.4 (LTS) or 7.5 works.
    2. Pin to a previous specific version of Az and Az.Accounts as a workaround before option 1 is applied. Here's a guide for that: https://gist.github.com/isra-fel/2451dc54260ff7e3021e552054a2a42c

    Our use of Az.Accounts is buried in a docker file so we went with #2 but ran into Az.Accounts still being updating to 5.3.0 and found a workaround by running uninstall to get rid of it.

    Install-Module -Name Az.Accounts -Repository PSGallery -Force -RequiredVersion 5.2.0
    Import-Module -Name Az.Accounts -Force -RequiredVersion 5.2.0 -global
    Install-Module -Name Az.Storage -Repository PSGallery -Force -RequiredVersion 5.2.0
    # uninstalls 5.3.0 which only leaves 5.2.0
    Uninstall-module az.accounts
    Import-Module -Name Az.Storage -Force -RequiredVersion 5.2.0 -global
    
  11. Mike-Gouldthorp commented on Sep 24, 2025

    @Mike-Gouldthorp

    This issue has been closed but I can confirm we are still experiencing the issue with the Az.Accounts module (version 5.3.0) when using the Azure DevOps "Azure PowerShell" task (5.*).

    Removing 5.3.0 and installing 5.2.0 might work but its not fixed. That is just a work around.

  12. isra-fel commented on Sep 29, 2025

    @isra-fel
    Member

    Hi Mike-Gouldthorp as stated above, the root cause is that the module is running on an unsupported PowerShell version (< 7.4). Please double check if you are running a self-hosted agent pool and if yes what version of PowerShell are you on.

  13. shenglol commented on Sep 29, 2025

    @shenglol
    Contributor

    Yeming Liu (@isra-fel) We use PowerShell 7.4 on Ubuntu 24.04 but we are still getting the error. Currently our workaround is to pin Az.Accounts to 5.2.0.

    Sorry, I was looking at the wrong image earlier. We’re actually running PowerShell 7.2, which explains the error. Upgrading PowerShell would be a breaking change for us, so we had to pin Az.Accounts to an older version.

    What’s surprising is that although we’re using Az < 14.0, which depends on Az.Accounts 4.x, Az.Accounts 5.3 still gets installed. This suggests that Az always pulls in the latest Az.Accounts, even when the Az module is older, and that the >= dependency applies across major versions as well.

    My concern is that this makes Az installations non-deterministic. If Az.Accounts raises its runtime requirements in the future, older Az versions could break again. Do we have any plans to address this?

  14. shenglol commented on Sep 29, 2025

    @shenglol
    Contributor

    I know we cannot patch the old Az PowerShell version, but I wonder if we can force modules depending on Az.Accounts to set RequiredModules.MaximumVersion to ensure back-compatibility going forward.

  15. Mike-Gouldthorp commented on Feb 18, 2026

    @Mike-Gouldthorp

    Yeming Liu (@isra-fel) Can you provide specific Microsoft documentation that states Az.Accounts 5.3.x requires PowerShell > 7.4?

  16. jikuja commented on Aug 18, 2026

    @jikuja

    Check out PowerShell end-of-support dates for details. As of today, the supported versions of PowerShell are 7.4 and 7.5.

    This should be communicated properly.

    https://learn.microsoft.com/en-us/powershell/azure/what-is-azure-powershell?view=azps-16.2.0#the-az-powershell-module:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Azure PS TeambugThis issue requires a change to an existing behavior in the product in order to be resolved.customer-reported

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions