From 269dcb31422af89e70d728d888e422c4923b8356 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 6 Feb 2026 09:37:41 +0100 Subject: [PATCH 1/9] Remove inline powershell for getting credentials --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 45 ++++++++ Actions/CheckAuthContext/README.md | 32 ++++++ Actions/CheckAuthContext/action.yaml | 34 ++++++ .../CreateOnlineDevelopmentEnvironment.yaml | 24 +--- .../workflows/PublishToEnvironment.yaml | 36 ++---- .../CreateOnlineDevelopmentEnvironment.yaml | 24 +--- .../workflows/PublishToEnvironment.yaml | 36 ++---- Tests/CheckAuthContext.Action.Test.ps1 | 107 ++++++++++++++++++ 8 files changed, 244 insertions(+), 94 deletions(-) create mode 100644 Actions/CheckAuthContext/CheckAuthContext.ps1 create mode 100644 Actions/CheckAuthContext/README.md create mode 100644 Actions/CheckAuthContext/action.yaml create mode 100644 Tests/CheckAuthContext.Action.Test.ps1 diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 new file mode 100644 index 0000000000..8790c35f49 --- /dev/null +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -0,0 +1,45 @@ +Param( + [Parameter(HelpMessage = "Name of the secret to check (e.g., 'adminCenterApiCredentials' or comma-separated list to check multiple)", Mandatory = $true)] + [string] $secretName, + [Parameter(HelpMessage = "Environment name (for error messages)", Mandatory = $false)] + [string] $environmentName = '' +) + +. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) + +$settings = $env:Settings | ConvertFrom-Json +$secrets = $env:Secrets | ConvertFrom-Json | ConvertTo-HashTable + +# Check each secret name in order +$authContext = $null +$foundSecretName = '' +foreach ($name in ($secretName -split ',')) { + $name = $name.Trim() + if ($secrets.ContainsKey($name) -and $secrets."$name") { + Write-Host "Using $name secret" + $authContext = $secrets."$name" + $foundSecretName = $name + break + } +} + +if ($authContext) { + Write-Host "AuthContext provided in secret $foundSecretName!" + Add-Content -Encoding UTF8 -Path $ENV:GITHUB_STEP_SUMMARY -Value "AuthContext was provided in a secret called $foundSecretName. Using this information for authentication." +} +else { + Write-Host "No AuthContext provided, initiating Device Code flow" + DownloadAndImportBcContainerHelper + $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) + + # Build appropriate error message + if ($environmentName) { + $message = "AL-Go needs access to the Business Central Environment $($environmentName.Split(' ')[0]) and could not locate a secret called $($secretName -replace ',', ' or ')" + } + else { + $message = "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($settings.adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)" + } + + Add-Content -Encoding UTF8 -Path $ENV:GITHUB_STEP_SUMMARY -Value "$message`n`n$($authContext.message)" + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)" +} diff --git a/Actions/CheckAuthContext/README.md b/Actions/CheckAuthContext/README.md new file mode 100644 index 0000000000..adafed8c39 --- /dev/null +++ b/Actions/CheckAuthContext/README.md @@ -0,0 +1,32 @@ +# Check Auth Context + +Check if Admin Center Api Credentials / AuthContext are provided in secrets. If not, initiate device code flow for authentication. + +## INPUT + +### ENV variables + +| Name | Description | +| :-- | :-- | +| Settings | env.Settings must be set by a prior call to the ReadSettings Action | +| Secrets | env.Secrets must be set to the secrets output from ReadSecrets Action | + +### Parameters + +| Name | Required | Description | Default value | +| :-- | :-: | :-- | :-- | +| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | +| secretName | Yes | Name of the secret to check (comma-separated list to check multiple in order) | | +| environmentName | | Environment name (for error messages when deploying to environments) | | + +## OUTPUT + +### ENV variables + +none + +### OUTPUT variables + +| Name | Description | +| :-- | :-- | +| deviceCode | Device code for authentication (only set if device login is required) | diff --git a/Actions/CheckAuthContext/action.yaml b/Actions/CheckAuthContext/action.yaml new file mode 100644 index 0000000000..e55d7f89f8 --- /dev/null +++ b/Actions/CheckAuthContext/action.yaml @@ -0,0 +1,34 @@ +name: Check Auth Context +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: powershell + secretName: + description: Name of the secret to check (e.g., 'adminCenterApiCredentials' or comma-separated list to check multiple) + required: true + environmentName: + description: Environment name (for error messages) + required: false + default: '' +outputs: + deviceCode: + description: Device code for authentication (if device login is required) + value: ${{ steps.CheckAuthContext.outputs.deviceCode }} +runs: + using: composite + steps: + - name: run + shell: ${{ inputs.shell }} + id: CheckAuthContext + env: + _secretName: ${{ inputs.secretName }} + _environmentName: ${{ inputs.environmentName }} + run: | + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "CheckAuthContext" -Action { + ${{ github.action_path }}/CheckAuthContext.ps1 -secretName $ENV:_secretName -environmentName $ENV:_environmentName + } +branding: + icon: terminal + color: blue diff --git a/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml b/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml index 174741887b..2c52fa88ae 100644 --- a/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml +++ b/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml @@ -79,24 +79,12 @@ jobs: - name: Check AdminCenterApiCredentials / Initiate Device Login (open to see code) id: authenticate - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $settings = $env:Settings | ConvertFrom-Json - if ('${{ fromJson(steps.ReadSecrets.outputs.Secrets).adminCenterApiCredentials }}') { - Write-Host "AdminCenterApiCredentials provided in secret $($settings.adminCenterApiCredentialsSecretName)!" - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "Admin Center Api Credentials was provided in a secret called $($settings.adminCenterApiCredentialsSecretName). Using this information for authentication." - } - else { - Write-Host "AdminCenterApiCredentials not provided, initiating Device Code flow" - $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1" - $webClient = New-Object System.Net.WebClient - $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/main/AL-Go-Helper.ps1', $ALGoHelperPath) - . $ALGoHelperPath - DownloadAndImportBcContainerHelper - $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($settings.adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)`n`n$($authContext.message)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)" - } + uses: microsoft/AL-Go-Actions/CheckAuthContext@main + env: + Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' + with: + shell: powershell + secretName: 'adminCenterApiCredentials' CreateDevelopmentEnvironment: needs: [ Initialization ] diff --git a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml index 710d401195..3d7cfc7ffc 100644 --- a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml @@ -92,35 +92,13 @@ jobs: - name: Authenticate id: Authenticate if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1 - run: | - $envName = '${{ steps.envName.outputs.envName }}' - $secretName = '' - $secrets = '${{ steps.ReadSecrets.outputs.Secrets }}' | ConvertFrom-Json - $authContext = $null - "$($envName)-AuthContext", "$($envName)_AuthContext", "AuthContext" | ForEach-Object { - if (!($authContext)) { - if ($secrets."$_") { - Write-Host "Using $_ secret as AuthContext" - $authContext = $secrets."$_" - $secretName = $_ - } - } - } - if ($authContext) { - Write-Host "AuthContext provided in secret $secretName!" - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AuthContext was provided in a secret called $secretName. Using this information for authentication." - } - else { - Write-Host "No AuthContext provided for $envName, initiating Device Code flow" - $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1" - $webClient = New-Object System.Net.WebClient - $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/main/AL-Go-Helper.ps1', $ALGoHelperPath) - . $ALGoHelperPath - DownloadAndImportBcContainerHelper - $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Environment $('${{ steps.envName.outputs.envName }}'.Split(' ')[0]) and could not locate a secret called ${{ steps.envName.outputs.envName }}_AuthContext`n`n$($authContext.message)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)" - } + uses: microsoft/AL-Go-Actions/CheckAuthContext@main + env: + Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' + with: + shell: powershell + secretName: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext' + environmentName: ${{ steps.envName.outputs.envName }} Deploy: needs: [ Initialization ] diff --git a/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml b/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml index 174741887b..2c52fa88ae 100644 --- a/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml @@ -79,24 +79,12 @@ jobs: - name: Check AdminCenterApiCredentials / Initiate Device Login (open to see code) id: authenticate - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $settings = $env:Settings | ConvertFrom-Json - if ('${{ fromJson(steps.ReadSecrets.outputs.Secrets).adminCenterApiCredentials }}') { - Write-Host "AdminCenterApiCredentials provided in secret $($settings.adminCenterApiCredentialsSecretName)!" - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "Admin Center Api Credentials was provided in a secret called $($settings.adminCenterApiCredentialsSecretName). Using this information for authentication." - } - else { - Write-Host "AdminCenterApiCredentials not provided, initiating Device Code flow" - $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1" - $webClient = New-Object System.Net.WebClient - $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/main/AL-Go-Helper.ps1', $ALGoHelperPath) - . $ALGoHelperPath - DownloadAndImportBcContainerHelper - $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($settings.adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)`n`n$($authContext.message)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)" - } + uses: microsoft/AL-Go-Actions/CheckAuthContext@main + env: + Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' + with: + shell: powershell + secretName: 'adminCenterApiCredentials' CreateDevelopmentEnvironment: needs: [ Initialization ] diff --git a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml index 710d401195..3d7cfc7ffc 100644 --- a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml @@ -92,35 +92,13 @@ jobs: - name: Authenticate id: Authenticate if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1 - run: | - $envName = '${{ steps.envName.outputs.envName }}' - $secretName = '' - $secrets = '${{ steps.ReadSecrets.outputs.Secrets }}' | ConvertFrom-Json - $authContext = $null - "$($envName)-AuthContext", "$($envName)_AuthContext", "AuthContext" | ForEach-Object { - if (!($authContext)) { - if ($secrets."$_") { - Write-Host "Using $_ secret as AuthContext" - $authContext = $secrets."$_" - $secretName = $_ - } - } - } - if ($authContext) { - Write-Host "AuthContext provided in secret $secretName!" - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AuthContext was provided in a secret called $secretName. Using this information for authentication." - } - else { - Write-Host "No AuthContext provided for $envName, initiating Device Code flow" - $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1" - $webClient = New-Object System.Net.WebClient - $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/main/AL-Go-Helper.ps1', $ALGoHelperPath) - . $ALGoHelperPath - DownloadAndImportBcContainerHelper - $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) - Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Environment $('${{ steps.envName.outputs.envName }}'.Split(' ')[0]) and could not locate a secret called ${{ steps.envName.outputs.envName }}_AuthContext`n`n$($authContext.message)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)" - } + uses: microsoft/AL-Go-Actions/CheckAuthContext@main + env: + Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' + with: + shell: powershell + secretName: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext' + environmentName: ${{ steps.envName.outputs.envName }} Deploy: needs: [ Initialization ] diff --git a/Tests/CheckAuthContext.Action.Test.ps1 b/Tests/CheckAuthContext.Action.Test.ps1 new file mode 100644 index 0000000000..5df078f3a0 --- /dev/null +++ b/Tests/CheckAuthContext.Action.Test.ps1 @@ -0,0 +1,107 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') +$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +Describe "CheckAuthContext Action Tests" { + BeforeAll { + $actionName = "CheckAuthContext" + $scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve + $scriptName = "$actionName.ps1" + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $scriptRoot $scriptName + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'actionScript', Justification = 'False positive.')] + $actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName + } + + It 'Compile Action' { + Invoke-Expression $actionScript + } + + It 'Test action.yaml matches script' { + $outputs = [ordered]@{ + "deviceCode" = "Device code for authentication (if device login is required)" + } + YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript -outputs $outputs + } + + It 'Should find first matching secret' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{"adminCenterApiCredentials": "someCredentials"}' + $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() + $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'adminCenterApiCredentials' + + # Should NOT output deviceCode when secret is found + $output = Get-Content $env:GITHUB_OUTPUT -Raw + $output | Should -Not -Match "deviceCode=" + + # Should write to step summary + $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw + $summary | Should -Match "AuthContext was provided in a secret called adminCenterApiCredentials" + + Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue + Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue + } + + It 'Should find secret when checking multiple names - first match wins' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{"TestEnv-AuthContext": "firstSecret", "AuthContext": "secondSecret"}' + $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() + $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' + + $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw + $summary | Should -Match "AuthContext was provided in a secret called TestEnv-AuthContext" + + Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue + Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue + } + + It 'Should find fallback secret when primary not found' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{"AuthContext": "fallbackSecret"}' + $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() + $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' + + $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw + $summary | Should -Match "AuthContext was provided in a secret called AuthContext" + + Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue + Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue + } + + It 'Should initiate device login when no secret is found' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{}' + $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() + $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() + + # Import AL-Go-Helper to get the functions defined, then mock them + . (Join-Path $scriptRoot "..\AL-Go-Helper.ps1") + Mock DownloadAndImportBcContainerHelper { } + Mock New-BcAuthContext { + return @{ deviceCode = "TESTDEVICECODE"; message = "Enter code to authenticate" } + } + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'nonExistentSecret' + + # Should output deviceCode when no secret is found + $output = Get-Content $env:GITHUB_OUTPUT -Raw + $output | Should -Match "deviceCode=TESTDEVICECODE" + + # Should write device login message to step summary + $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw + $summary | Should -Match "could not locate a secret" + + Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue + Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue + } +} From f2ca058c01a3b2ff7fea33ada938651196f53c4a Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 6 Feb 2026 09:38:28 +0100 Subject: [PATCH 2/9] releasenotes --- RELEASENOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index aab396709e..228aa0d0ad 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,7 @@ +### Issues + +- Issue 2113 Using the action "Create Online Dev. Environment" fails in Initialization phase + ## v8.2 ### Issues From 59656ff3496d29faae5c18c44113024d7f15e9e0 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Wed, 18 Feb 2026 07:52:22 +0100 Subject: [PATCH 3/9] Remove output log --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 8790c35f49..69aae52439 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -24,10 +24,8 @@ foreach ($name in ($secretName -split ',')) { } if ($authContext) { - Write-Host "AuthContext provided in secret $foundSecretName!" - Add-Content -Encoding UTF8 -Path $ENV:GITHUB_STEP_SUMMARY -Value "AuthContext was provided in a secret called $foundSecretName. Using this information for authentication." -} -else { + Write-Host "AuthContext provided in secret $foundSecretName! Using this information for authentication." +} else { Write-Host "No AuthContext provided, initiating Device Code flow" DownloadAndImportBcContainerHelper $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) From 57b2b47cc7b8a4b2b56df4529eaa3271179f0fe3 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Wed, 18 Feb 2026 08:40:29 +0100 Subject: [PATCH 4/9] Update tests --- Tests/CheckAuthContext.Action.Test.ps1 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Tests/CheckAuthContext.Action.Test.ps1 b/Tests/CheckAuthContext.Action.Test.ps1 index 5df078f3a0..1fb2d796a4 100644 --- a/Tests/CheckAuthContext.Action.Test.ps1 +++ b/Tests/CheckAuthContext.Action.Test.ps1 @@ -37,10 +37,6 @@ Describe "CheckAuthContext Action Tests" { $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Not -Match "deviceCode=" - # Should write to step summary - $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw - $summary | Should -Match "AuthContext was provided in a secret called adminCenterApiCredentials" - Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue } @@ -54,8 +50,9 @@ Describe "CheckAuthContext Action Tests" { Invoke-Expression $actionScript CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' - $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw - $summary | Should -Match "AuthContext was provided in a secret called TestEnv-AuthContext" + # Should NOT output deviceCode when secret is found + $output = Get-Content $env:GITHUB_OUTPUT -Raw + $output | Should -Not -Match "deviceCode=" Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue @@ -70,8 +67,9 @@ Describe "CheckAuthContext Action Tests" { Invoke-Expression $actionScript CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' - $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw - $summary | Should -Match "AuthContext was provided in a secret called AuthContext" + # Should NOT output deviceCode when secret is found + $output = Get-Content $env:GITHUB_OUTPUT -Raw + $output | Should -Not -Match "deviceCode=" Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue From ce0f02b93b26f91370761495094140f77b51ffac Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 19 Feb 2026 15:26:49 +0100 Subject: [PATCH 5/9] Implement feedback --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 5 ++++ Tests/CheckAuthContext.Action.Test.ps1 | 30 +++++++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 69aae52439..9e82d22b5c 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -5,6 +5,8 @@ Param( [string] $environmentName = '' ) +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) $settings = $env:Settings | ConvertFrom-Json @@ -29,6 +31,9 @@ if ($authContext) { Write-Host "No AuthContext provided, initiating Device Code flow" DownloadAndImportBcContainerHelper $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) + if ($null -eq $authContext) { + throw "Failed to acquire authentication context via device code flow" + } # Build appropriate error message if ($environmentName) { diff --git a/Tests/CheckAuthContext.Action.Test.ps1 b/Tests/CheckAuthContext.Action.Test.ps1 index 1fb2d796a4..88f7583b0b 100644 --- a/Tests/CheckAuthContext.Action.Test.ps1 +++ b/Tests/CheckAuthContext.Action.Test.ps1 @@ -13,6 +13,16 @@ Describe "CheckAuthContext Action Tests" { $actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName } + BeforeEach { + $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() + $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() + } + + AfterEach { + Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue + Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue + } + It 'Compile Action' { Invoke-Expression $actionScript } @@ -27,8 +37,6 @@ Describe "CheckAuthContext Action Tests" { It 'Should find first matching secret' { $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' $env:Secrets = '{"adminCenterApiCredentials": "someCredentials"}' - $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() - $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() Invoke-Expression $actionScript CheckAuthContext -secretName 'adminCenterApiCredentials' @@ -36,16 +44,11 @@ Describe "CheckAuthContext Action Tests" { # Should NOT output deviceCode when secret is found $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Not -Match "deviceCode=" - - Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue - Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue } It 'Should find secret when checking multiple names - first match wins' { $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' $env:Secrets = '{"TestEnv-AuthContext": "firstSecret", "AuthContext": "secondSecret"}' - $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() - $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() Invoke-Expression $actionScript CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' @@ -53,16 +56,11 @@ Describe "CheckAuthContext Action Tests" { # Should NOT output deviceCode when secret is found $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Not -Match "deviceCode=" - - Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue - Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue } It 'Should find fallback secret when primary not found' { $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' $env:Secrets = '{"AuthContext": "fallbackSecret"}' - $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() - $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() Invoke-Expression $actionScript CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' @@ -70,16 +68,11 @@ Describe "CheckAuthContext Action Tests" { # Should NOT output deviceCode when secret is found $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Not -Match "deviceCode=" - - Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue - Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue } It 'Should initiate device login when no secret is found' { $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' $env:Secrets = '{}' - $env:GITHUB_STEP_SUMMARY = [System.IO.Path]::GetTempFileName() - $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() # Import AL-Go-Helper to get the functions defined, then mock them . (Join-Path $scriptRoot "..\AL-Go-Helper.ps1") @@ -98,8 +91,5 @@ Describe "CheckAuthContext Action Tests" { # Should write device login message to step summary $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw $summary | Should -Match "could not locate a secret" - - Remove-Item $env:GITHUB_STEP_SUMMARY -ErrorAction SilentlyContinue - Remove-Item $env:GITHUB_OUTPUT -ErrorAction SilentlyContinue } } From ecef88e73a6b31bc8594002fbc088f4c61003537 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 19 Feb 2026 15:57:13 +0100 Subject: [PATCH 6/9] Feedback --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 2 +- Tests/CheckAuthContext.Action.Test.ps1 | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 9e82d22b5c..6a7545b312 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -40,7 +40,7 @@ if ($authContext) { $message = "AL-Go needs access to the Business Central Environment $($environmentName.Split(' ')[0]) and could not locate a secret called $($secretName -replace ',', ' or ')" } else { - $message = "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($settings.adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)" + $message = "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($secretName -replace ',', ' or ') (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)" } Add-Content -Encoding UTF8 -Path $ENV:GITHUB_STEP_SUMMARY -Value "$message`n`n$($authContext.message)" diff --git a/Tests/CheckAuthContext.Action.Test.ps1 b/Tests/CheckAuthContext.Action.Test.ps1 index 88f7583b0b..2572a14714 100644 --- a/Tests/CheckAuthContext.Action.Test.ps1 +++ b/Tests/CheckAuthContext.Action.Test.ps1 @@ -50,12 +50,17 @@ Describe "CheckAuthContext Action Tests" { $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' $env:Secrets = '{"TestEnv-AuthContext": "firstSecret", "AuthContext": "secondSecret"}' + Mock Write-Host {} + Invoke-Expression $actionScript CheckAuthContext -secretName 'TestEnv-AuthContext,TestEnv_AuthContext,AuthContext' # Should NOT output deviceCode when secret is found $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Not -Match "deviceCode=" + + # Should use the first matching secret, not a later one + Should -Invoke Write-Host -ParameterFilter { $Object -eq "Using TestEnv-AuthContext secret" } } It 'Should find fallback secret when primary not found' { @@ -84,6 +89,9 @@ Describe "CheckAuthContext Action Tests" { Invoke-Expression $actionScript CheckAuthContext -secretName 'nonExistentSecret' + # Should invoke New-BcAuthContext to get device code + Should -Invoke New-BcAuthContext -Exactly -Times 1 + # Should output deviceCode when no secret is found $output = Get-Content $env:GITHUB_OUTPUT -Raw $output | Should -Match "deviceCode=TESTDEVICECODE" From e898b557d4aa85873be4a2ce3939c181bc94bcf0 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 19 Feb 2026 16:03:06 +0100 Subject: [PATCH 7/9] more test coverage --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 3 +- Tests/CheckAuthContext.Action.Test.ps1 | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 6a7545b312..9b46c90419 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -32,7 +32,8 @@ if ($authContext) { DownloadAndImportBcContainerHelper $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) if ($null -eq $authContext) { - throw "Failed to acquire authentication context via device code flow" + OutputError "Failed to acquire authentication context via device code flow." + return } # Build appropriate error message diff --git a/Tests/CheckAuthContext.Action.Test.ps1 b/Tests/CheckAuthContext.Action.Test.ps1 index 2572a14714..403de8f1eb 100644 --- a/Tests/CheckAuthContext.Action.Test.ps1 +++ b/Tests/CheckAuthContext.Action.Test.ps1 @@ -100,4 +100,37 @@ Describe "CheckAuthContext Action Tests" { $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw $summary | Should -Match "could not locate a secret" } + + It 'Should output error when New-BcAuthContext returns null' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{}' + + . (Join-Path $scriptRoot "..\AL-Go-Helper.ps1") + Mock DownloadAndImportBcContainerHelper { } + Mock New-BcAuthContext { return $null } + Mock OutputError { } + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'nonExistentSecret' + + Should -Invoke OutputError -ParameterFilter { $message -like "*Failed to acquire authentication*" } + } + + It 'Should use environment-specific message when environmentName is provided' { + $env:Settings = '{"adminCenterApiCredentialsSecretName": "adminCenterApiCredentials"}' + $env:Secrets = '{}' + + . (Join-Path $scriptRoot "..\AL-Go-Helper.ps1") + Mock DownloadAndImportBcContainerHelper { } + Mock New-BcAuthContext { + return @{ deviceCode = "TESTDEVICECODE"; message = "Enter code to authenticate" } + } + + Invoke-Expression $actionScript + CheckAuthContext -secretName 'secret1,secret2' -environmentName 'MyEnv (Production)' + + $summary = Get-Content $env:GITHUB_STEP_SUMMARY -Raw + $summary | Should -Match "Business Central Environment MyEnv" + $summary | Should -Match "secret1 or secret2" + } } From c1ca38638b1bbc4ec553b3480b09bbaca17905ac Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 19 Feb 2026 16:21:18 +0100 Subject: [PATCH 8/9] Cleanup --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 3 --- 1 file changed, 3 deletions(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 9b46c90419..113e41af95 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -5,11 +5,8 @@ Param( [string] $environmentName = '' ) -$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) -$settings = $env:Settings | ConvertFrom-Json $secrets = $env:Secrets | ConvertFrom-Json | ConvertTo-HashTable # Check each secret name in order From 38f7148dadb727e5ce6a3760e58659ce41ef419c Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Mon, 23 Feb 2026 13:09:09 +0100 Subject: [PATCH 9/9] authType parameter --- Actions/CheckAuthContext/CheckAuthContext.ps1 | 6 ++++-- Actions/CheckAuthContext/README.md | 1 + Actions/CheckAuthContext/action.yaml | 7 ++++++- .../workflows/CreateOnlineDevelopmentEnvironment.yaml | 1 + .../workflows/CreateOnlineDevelopmentEnvironment.yaml | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Actions/CheckAuthContext/CheckAuthContext.ps1 b/Actions/CheckAuthContext/CheckAuthContext.ps1 index 113e41af95..b83a9f7d0a 100644 --- a/Actions/CheckAuthContext/CheckAuthContext.ps1 +++ b/Actions/CheckAuthContext/CheckAuthContext.ps1 @@ -1,6 +1,8 @@ Param( [Parameter(HelpMessage = "Name of the secret to check (e.g., 'adminCenterApiCredentials' or comma-separated list to check multiple)", Mandatory = $true)] [string] $secretName, + [Parameter(HelpMessage = "Type of authentication (e.g., 'AuthContext' or 'Admin Center Api Credentials')", Mandatory = $false)] + [string] $authType = 'AuthContext', [Parameter(HelpMessage = "Environment name (for error messages)", Mandatory = $false)] [string] $environmentName = '' ) @@ -23,9 +25,9 @@ foreach ($name in ($secretName -split ',')) { } if ($authContext) { - Write-Host "AuthContext provided in secret $foundSecretName! Using this information for authentication." + Write-Host "$authType provided in secret $foundSecretName! Using this information for authentication." } else { - Write-Host "No AuthContext provided, initiating Device Code flow" + Write-Host "No $authType provided, initiating Device Code flow" DownloadAndImportBcContainerHelper $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0)) if ($null -eq $authContext) { diff --git a/Actions/CheckAuthContext/README.md b/Actions/CheckAuthContext/README.md index adafed8c39..255095be5b 100644 --- a/Actions/CheckAuthContext/README.md +++ b/Actions/CheckAuthContext/README.md @@ -17,6 +17,7 @@ Check if Admin Center Api Credentials / AuthContext are provided in secrets. If | :-- | :-: | :-- | :-- | | shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | | secretName | Yes | Name of the secret to check (comma-separated list to check multiple in order) | | +| authType | | Type of authentication (e.g., 'AuthContext' or 'Admin Center Api Credentials') | AuthContext | | environmentName | | Environment name (for error messages when deploying to environments) | | ## OUTPUT diff --git a/Actions/CheckAuthContext/action.yaml b/Actions/CheckAuthContext/action.yaml index e55d7f89f8..3014717b3c 100644 --- a/Actions/CheckAuthContext/action.yaml +++ b/Actions/CheckAuthContext/action.yaml @@ -8,6 +8,10 @@ inputs: secretName: description: Name of the secret to check (e.g., 'adminCenterApiCredentials' or comma-separated list to check multiple) required: true + authType: + description: Type of authentication (e.g., 'AuthContext' or 'Admin Center Api Credentials') + required: false + default: 'AuthContext' environmentName: description: Environment name (for error messages) required: false @@ -24,10 +28,11 @@ runs: id: CheckAuthContext env: _secretName: ${{ inputs.secretName }} + _authType: ${{ inputs.authType }} _environmentName: ${{ inputs.environmentName }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "CheckAuthContext" -Action { - ${{ github.action_path }}/CheckAuthContext.ps1 -secretName $ENV:_secretName -environmentName $ENV:_environmentName + ${{ github.action_path }}/CheckAuthContext.ps1 -secretName $ENV:_secretName -authType $ENV:_authType -environmentName $ENV:_environmentName } branding: icon: terminal diff --git a/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml b/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml index 2c52fa88ae..054b48e271 100644 --- a/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml +++ b/Templates/AppSource App/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml @@ -85,6 +85,7 @@ jobs: with: shell: powershell secretName: 'adminCenterApiCredentials' + authType: 'Admin Center Api Credentials' CreateDevelopmentEnvironment: needs: [ Initialization ] diff --git a/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml b/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml index 2c52fa88ae..054b48e271 100644 --- a/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml @@ -85,6 +85,7 @@ jobs: with: shell: powershell secretName: 'adminCenterApiCredentials' + authType: 'Admin Center Api Credentials' CreateDevelopmentEnvironment: needs: [ Initialization ]