diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 27a83c107..f9bb67a67 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -@kaverma @kanika1894 @BALAGA-GAYATRI @pulkitaggarwl +@Azure/act-identity-squad diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..b545d1322 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 5 + groups: + dev-dependencies: + dependency-type: "development" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 5 + groups: + github-actions: + patterns: ["*"] diff --git a/.github/workflows/azure-login-canary.yml b/.github/workflows/azure-login-canary.yml index 5c430f703..665e04432 100644 --- a/.github/workflows/azure-login-canary.yml +++ b/.github/workflows/azure-login-canary.yml @@ -30,10 +30,10 @@ jobs: az --version - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Az CLI login with subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} @@ -41,7 +41,7 @@ jobs: az account show --output none - name: 'Az CLI login without subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} allow-no-subscriptions: true @@ -50,7 +50,7 @@ jobs: az account show --output none - name: 'Az CLI login with subscription OIDC' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} @@ -60,7 +60,7 @@ jobs: az account show --output none - name: 'Az CLI login without subscription OIDC' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} @@ -68,25 +68,3 @@ jobs: - run: | az account show --output none - - slack-post-result: - runs-on: ubuntu-latest -# continue-on-error: true - if: ${{ always() }} - needs: [az-login-test] - steps: - - name: Create slack post - id: slack_report - run: | - TITLE="Login action canary tests update - " - DATEVAR=`date "+%d/%m/%YT%H:%M:%S"` - TITLE="${TITLE}${DATEVAR}" - REPORT="${TITLE}\r\nLink to run - https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\r\n" - RUN_URL="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" - REPORT="${REPORT}\r\n" - if [ ${{needs.az-login-test.result}} == 'success' ]; then REPORT="${REPORT}\r\n|✅|<${RUN_URL}|az-login-test>"; else REPORT="${REPORT}\r\n|❌|<${RUN_URL}|az-login-test>"; fi - echo "report=$REPORT" >> $GITHUB_OUTPUT - - name: Post to slack - shell: bash - run: curl -X POST -H 'Content-type:application/json' --data '{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":"${{steps.slack_report.outputs.report}}"}}]}' https://hooks.slack.com/services/${{SECRETS.SLACK_CHANNEL_SECRET}} - diff --git a/.github/workflows/azure-login-integration-tests.yml b/.github/workflows/azure-login-integration-tests.yml index 120b5a71b..31b91186e 100644 --- a/.github/workflows/azure-login-integration-tests.yml +++ b/.github/workflows/azure-login-integration-tests.yml @@ -14,7 +14,7 @@ jobs: # continue-on-error: true steps: - name: 'Az CLI login with subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} @@ -23,7 +23,7 @@ jobs: az vm list --output none - name: 'Az CLI login without subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} allow-no-subscriptions: true @@ -32,24 +32,24 @@ jobs: az account show --output none - name: 'Azure PowerShell login with subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - - uses: azure/powershell@v1 + - uses: azure/powershell@v3 with: inlineScript: "(Get-AzContext).Environment.Name" azPSVersion: "latest" - name: 'Azure PowerShell login without subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: creds: ${{secrets.AZURE_CREDENTIALS}} enable-AzPSSession: true allow-no-subscriptions: true - - uses: azure/powershell@v1 + - uses: azure/powershell@v3 with: inlineScript: "(Get-AzContext).Environment.Name" azPSVersion: "latest" @@ -59,7 +59,7 @@ jobs: # continue-on-error: true steps: - name: 'Az CLI login with subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} @@ -70,7 +70,7 @@ jobs: az vm list --output none - name: 'Az CLI login without subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} @@ -80,50 +80,27 @@ jobs: az account show --output none - name: 'Azure PowerShell login with subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTIONID }} enable-AzPSSession: true - - uses: azure/powershell@v1 + - uses: azure/powershell@v3 with: inlineScript: "(Get-AzContext).Environment.Name" azPSVersion: "latest" - name: 'Azure PowerShell login without subscription' - uses: azure/login@v1 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENTID }} tenant-id: ${{ secrets.AZURE_TENANTID }} enable-AzPSSession: true allow-no-subscriptions: true - - uses: azure/powershell@v1 + - uses: azure/powershell@v3 with: inlineScript: "(Get-AzContext).Environment.Name" azPSVersion: "latest" - - slack-post-result: - runs-on: ubuntu-latest -# continue-on-error: true - if: ${{ always() }} - needs: [az-login-test-non-oidc, az-login-test-oidc] - steps: - - name: Create slack post - id: slack_report - run: | - TITLE="Login action OIDC flow tests update - " - DATEVAR=`date "+%d/%m/%YT%H:%M:%S"` - TITLE="${TITLE}${DATEVAR}" - REPORT="${TITLE}\r\nLink to run - https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\r\n" - RUN_URL="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" - REPORT="${REPORT}\r\n" - if [ ${{needs.az-login-test-non-oidc.result}} == 'success' ]; then REPORT="${REPORT}\r\n|✅|<${RUN_URL}|az-login-test-non-oidc>"; else REPORT="${REPORT}\r\n|❌|<${RUN_URL}|az-login-test-non-oidc>"; fi - if [ ${{needs.az-login-test-oidc.result}} == 'success' ]; then REPORT="${REPORT}\r\n|✅|<${RUN_URL}|az-login-test-oidc>"; else REPORT="${REPORT}\r\n|❌|<${RUN_URL}|az-login-test-oidc>"; fi - echo "report=$REPORT" >> $GITHUB_OUTPUT - - - name: Post to slack - shell: bash - run: curl -X POST -H 'Content-type:application/json' --data '{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":"${{steps.slack_report.outputs.report}}"}}]}' https://hooks.slack.com/services/${{SECRETS.SLACK_CHANNEL_SECRET}} diff --git a/.github/workflows/azure-login-live-tests.yml b/.github/workflows/azure-login-live-tests.yml new file mode 100644 index 000000000..1d3657a95 --- /dev/null +++ b/.github/workflows/azure-login-live-tests.yml @@ -0,0 +1,693 @@ +name: Azure Login Action Live Tests +on: + workflow_dispatch: + pull_request: + branches: + - master + +permissions: + id-token: write + contents: read + +# Each run provisions its own uniquely-named resource group (suffixed with the +# run id), so concurrent runs on different branches/PRs never collide or tear +# down each other's group. The concurrency group is branch-scoped only to avoid +# piling up runs on rapid pushes to the same ref - it does NOT serialize across +# the whole repo, so an unapproved run on one PR can never block another. +concurrency: + group: azure-login-live-tests-${{ github.ref }} + cancel-in-progress: false + +env: + # Created once in setup; the positive and embedded "can access" assertions + # target this group. Suffixed with the run id so every run is isolated. + RG_POSITIVE: GitHubAction_CI_Group_${{ github.run_id }} + LOCATION: eastus + # Intentionally NEVER created: the negative permission/not-found assertions + # target this name so they fail as expected. + RG_NEGATIVE: GitHubAction_CI_RG + +jobs: + # ---------------------------------------------------------------- setup + # Provision the resource group the "can access" assertions target. Uses a + # repo-level provisioning credential (PROVISION_CREDS) and does NOT declare an + # environment, so it is not subject to the Automation test approval gate - + # setup/teardown are provisioning, not part of the gated test surface. + setup: + runs-on: ubuntu-latest + steps: + - name: Azure login (provisioning) + uses: azure/login@v3 + with: + creds: ${{ secrets.PROVISION_CREDS }} + - name: Create resource group + run: az group create --name "${{ env.RG_POSITIVE }}" --location "${{ env.LOCATION }}" --output none + + # ================================================================ POSITIVE + Positive_BasicTest: + needs: setup + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + environment: Automation test + + steps: + - name: 'Checking out repo code' + uses: actions/checkout@v6 + + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: 'Validate build' + run: | + npm install + npm run build + + - name: 'Run L0 tests' + run: | + npm run test + + - name: Login with creds + uses: ./ + with: + creds: ${{secrets.SP1}} + enable-AzPSSession: true + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Run Azure PowerShell + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Login with individual parameters + uses: ./ + with: + client-id: ${{ secrets.SP1_CLIENT_ID }} + tenant-id: ${{ secrets.SP1_TENANT_ID }} + subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Run Azure Cli again + run: | + az account show --output none + + - name: Run Azure PowerShell again + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Login with explicit auth-type + uses: ./ + with: + creds: ${{secrets.SP1}} + auth-type: SERVICE_PRINCIPAL + enable-AzPSSession: true + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Run Azure PowerShell + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + Positive_ParameterTest: + needs: setup + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + environment: Automation test + + steps: + - name: 'Checking out repo code' + uses: actions/checkout@v6 + + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: 'Validate build' + run: | + npm install + npm run build + + - name: Login with creds, disable ps session + uses: ./ + with: + creds: ${{secrets.SP1}} + enable-AzPSSession: false + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Login with creds, wrong boolean value + uses: ./ + with: + creds: ${{secrets.SP1}} + enable-AzPSSession: notboolean + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Login with creds, allow no subscription + uses: ./ + with: + creds: ${{secrets.SP1}} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Run Azure PowerShell + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Login with individual parameters, allow no subscription + uses: ./ + with: + client-id: ${{ secrets.SP1_CLIENT_ID }} + tenant-id: ${{ secrets.SP1_TENANT_ID}} + subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Run Azure Cli again + run: | + az account show --output none + + - name: Run Azure PowerShell again + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Login with individual parameters, no subscription, allow no subscription + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Run Azure Cli + shell: pwsh + run: | + $checkResult = (az account list --output json | ConvertFrom-Json).Count -eq 2 + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Run Azure PowerShell + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + - name: Login with creds, no subscription, allow no subscription + uses: ./ + with: + creds: '{"clientId":"${{ secrets.OIDC_SP2_CLIENT_ID }}","clientSecret":"${{ secrets.SP2_CLIENT_SECRET }}","tenantId":"${{ secrets.OIDC_SP2_TENANT_ID }}"}' + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Run Azure Cli + run: | + az account show --output none + + - name: Run Azure PowerShell + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' + if(-not $checkResult){ + throw "Not all checks passed!" + } + + Positive_InDockerTest: + needs: setup + runs-on: ubuntu-latest + container: ubuntu:24.04 + environment: Automation test + steps: + - name: 'Checking out repo code' + uses: actions/checkout@v6 + + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: Install Azure CLI + run: | + apt-get update + apt-get install -y curl + curl -sL https://aka.ms/InstallAzureCLIDeb | bash + + - name: Check Azure CLI Version + run: | + az --version + + - name: Install Powershell + run: | + apt-get update + apt-get install -y wget apt-transport-https software-properties-common + wget -q "https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb" + dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + apt-get update + apt-get install -y powershell + + - name: Check Powershell Version + shell: pwsh + run: | + $PSVersionTable + + - name: Install Azure Powershell + shell: pwsh + run: | + Install-Module -Name Az -Repository PSGallery -Force + + - name: Check Azure Powershell Version + shell: pwsh + run: | + Get-Module -ListAvailable Az + + - name: 'Validate build' + run: | + npm install + npm run build + + - name: 'Run L0 tests' + run: | + npm run test + + - name: Login with individual parameters + uses: ./ + with: + client-id: ${{ secrets.SP1_CLIENT_ID }} + tenant-id: ${{ secrets.SP1_TENANT_ID }} + subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Run Azure Cli again + run: | + az group list --output none + + - name: Run Azure PowerShell again + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + $checkResult = Get-AzResourceGroup + + # ================================================================ NEGATIVE + Negative_PermissionTest: + needs: setup + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + environment: Automation test + + steps: + + - name: 'Checking out repo code' + uses: actions/checkout@v6 + + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: 'Validate build' + run: | + npm install + npm run build + + - name: Login with individual parameters + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + # subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Run Azure Cli + id: cli_3 + continue-on-error: true + run: | + az account show --output none + az group show --name GitHubAction_CI_RG --output none + + - name: Check Last step failed + if: steps.cli_3.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Run Azure PowerShell + id: ps_3 + continue-on-error: true + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + (Get-AzContext).Environment.Name -eq 'AzureCloud' + (Get-AzResourceGroup -Name GitHubAction_CI_RG).ResourceGroupName -eq 'GitHubAction_CI_RG' + + - name: Check Last step failed + if: steps.ps_3.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + Negative_ParameterTest: + needs: setup + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + environment: Automation test + + steps: + - name: 'Checking out repo code' + uses: actions/checkout@v6 + + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: 'Validate build' + run: | + npm install + npm run build + + - name: Login with creds, missing parameters in creds + id: login_4 + continue-on-error: true + uses: ./ + with: + creds: ${{secrets.SP3_NO_Secret}} + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_4.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with creds, wrong keys + id: login_5 + continue-on-error: true + uses: ./ + with: + creds: ${{secrets.SP4_Wrong_Key}} + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_5.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with creds, no creds or individual parameters + id: login_6 + continue-on-error: true + uses: ./ + with: + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_6.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with individual parameters, only client-id, no tenant-id, subscription-id + id: login_7 + continue-on-error: true + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_7.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with individual parameters, only tenant-id, subscription-id, no client-id + id: login_8 + continue-on-error: true + uses: ./ + with: + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_8.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with creds, disable ps session + uses: ./ + with: + creds: ${{secrets.SP1}} + enable-AzPSSession: false + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Run Azure PowerShell + id: ps_8 + continue-on-error: true + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + (Get-AzContext).Environment.Name -eq 'AzureCloud' + (Get-AzResourceGroup -Name $env:RG_POSITIVE).ResourceGroupName -eq $env:RG_POSITIVE + + - name: Check Last step failed + if: steps.ps_8.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with creds, wrong boolean value + uses: ./ + with: + creds: ${{secrets.SP1}} + enable-AzPSSession: notboolean + + - name: Run Azure Cli + run: | + az account show --output none + az group show --name "${{ env.RG_POSITIVE }}" --output none + + - name: Run Azure PowerShell + id: ps_9 + continue-on-error: true + uses: azure/powershell@v3 + with: + azPSVersion: "latest" + inlineScript: | + (Get-AzContext).Environment.Name -eq 'AzureCloud' + (Get-AzResourceGroup -Name $env:RG_POSITIVE).ResourceGroupName -eq $env:RG_POSITIVE + + - name: Check Last step failed + if: steps.ps_9.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with individual parameters, with a wrong audience + id: login_10 + continue-on-error: true + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} + audience: "https://github.com/actions" + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_10.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with tenant-level account, without allow-no-subscriptions + id: login_11 + continue-on-error: true + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_11.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + # SP1 is ignored and SP2 will be used for login, but it will fail since SP2 has no access to the given subscription + - name: Login with both creds and individual parameters + id: login_12 + continue-on-error: true + uses: ./ + with: + creds: ${{secrets.SP1}} + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_12.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login by OIDC with all info in creds + id: login_13 + continue-on-error: true + uses: ./ + with: + creds: ${{secrets.SP2}} + allow-no-subscriptions: true + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_13.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with individual parameters, no subscription-id, no allow-no-subscriptions + id: login_14 + continue-on-error: true + uses: ./ + with: + client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} + tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_14.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + - name: Login with creds, no subscription-id, no allow-no-subscriptions + id: login_15 + continue-on-error: true + uses: ./ + with: + creds: '{"clientId":"${{ secrets.OIDC_SP2_CLIENT_ID }}","clientSecret":"${{ secrets.SP2_CLIENT_SECRET }}","tenantId":"${{ secrets.OIDC_SP2_TENANT_ID }}"}' + enable-AzPSSession: true + + - name: Check Last step failed + if: steps.login_15.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Last action should fail but not. Please check it.') + + # ---------------------------------------------------------------- teardown + # Always delete the resource group created in setup, even if gate jobs failed, + # so nothing is left running (cost) between the infrequent runs. Runs + # synchronously (no --no-wait) so the group is fully gone before the run ends + # and the concurrency lock releases, preventing a delete from a finished run + # racing the setup of the next one. + teardown: + needs: [setup, Positive_BasicTest, Positive_ParameterTest, Positive_InDockerTest, Negative_PermissionTest, Negative_ParameterTest] + if: always() + runs-on: ubuntu-latest + steps: + - name: Azure login (provisioning) + uses: azure/login@v3 + with: + creds: ${{ secrets.PROVISION_CREDS }} + - name: Delete resource group + run: az group delete --name "${{ env.RG_POSITIVE }}" --yes --output none || true diff --git a/.github/workflows/azure-login-negative.yml b/.github/workflows/azure-login-negative.yml deleted file mode 100644 index 91dda5801..000000000 --- a/.github/workflows/azure-login-negative.yml +++ /dev/null @@ -1,335 +0,0 @@ -name: Azure Login Action Negative Test -on: - workflow_dispatch: - push: - -permissions: - id-token: write - contents: read - -jobs: - - PermissionTest: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - environment: Automation test - - steps: - - - name: 'Checking out repo code' - uses: actions/checkout@v4 - - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: 'Validate build' - run: | - npm install - npm run build - - - name: Login with individual parameters - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - # subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Run Azure Cli - id: cli_3 - continue-on-error: true - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Check Last step failed - if: steps.cli_3.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Run Azure PowerShell - id: ps_3 - continue-on-error: true - uses: azure/powershell@v1 - with: - azPSVersion: "latest" - inlineScript: | - (Get-AzContext).Environment.Name -eq 'AzureCloud' - (Get-AzResourceGroup -Name GitHubAction_CI_RG).ResourceGroupName -eq 'GitHubAction_CI_RG' - (Get-AzVM).Count -gt 0 - - - name: Check Last step failed - if: steps.ps_3.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - ParameterTest: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - environment: Automation test - - steps: - - name: 'Checking out repo code' - uses: actions/checkout@v4 - - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: 'Validate build' - run: | - npm install - npm run build - - - name: Login with creds, missing parameters in creds - id: login_4 - continue-on-error: true - uses: ./ - with: - creds: ${{secrets.SP3_NO_Secret}} - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_4.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with creds, wrong keys - id: login_5 - continue-on-error: true - uses: ./ - with: - creds: ${{secrets.SP4_Wrong_Key}} - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_5.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with creds, no creds or individual parameters - id: login_6 - continue-on-error: true - uses: ./ - with: - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_6.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with individual parameters, only client-id, no tenant-id, subscription-id - id: login_7 - continue-on-error: true - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_7.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with individual parameters, only tenant-id, subscription-id, no client-id - id: login_8 - continue-on-error: true - uses: ./ - with: - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_8.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with creds, disable ps session - uses: ./ - with: - creds: ${{secrets.SP1}} - enable-AzPSSession: false - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Run Azure PowerShell - id: ps_8 - continue-on-error: true - uses: azure/powershell@v1 - with: - azPSVersion: "latest" - inlineScript: | - (Get-AzContext).Environment.Name -eq 'AzureCloud' - (Get-AzResourceGroup -Name GitHubAction_CI_RG).ResourceGroupName -eq 'GitHubAction_CI_RG' - (Get-AzVM).Count -gt 0 - - - name: Check Last step failed - if: steps.ps_8.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with creds, wrong boolean value - uses: ./ - with: - creds: ${{secrets.SP1}} - enable-AzPSSession: notboolean - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Run Azure PowerShell - id: ps_9 - continue-on-error: true - uses: azure/powershell@v1 - with: - azPSVersion: "latest" - inlineScript: | - (Get-AzContext).Environment.Name -eq 'AzureCloud' - (Get-AzResourceGroup -Name GitHubAction_CI_RG).ResourceGroupName -eq 'GitHubAction_CI_RG' - (Get-AzVM).Count -gt 0 - - - name: Check Last step failed - if: steps.ps_9.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with individual parameters, with a wrong audience - id: login_10 - continue-on-error: true - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} - audience: "https://github.com/actions" - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_10.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with tenant-level account, without allow-no-subscriptions - id: login_11 - continue-on-error: true - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_11.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - # SP1 is ignored and SP2 will be used for login, but it will fail since SP2 has no access to the given subscription - - name: Login with both creds and individual parameters - id: login_12 - continue-on-error: true - uses: ./ - with: - creds: ${{secrets.SP1}} - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - subscription-id: ${{ secrets.OIDC_SP2_SUBSCRIPTION_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_12.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login by OIDC with all info in creds - id: login_13 - continue-on-error: true - uses: ./ - with: - creds: ${{secrets.SP2}} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_13.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with individual parameters, no subscription-id, no allow-no-subscriptions - id: login_14 - continue-on-error: true - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_14.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') - - - name: Login with creds, no subscription-id, no allow-no-subscriptions - id: login_15 - continue-on-error: true - uses: ./ - with: - creds: '{"clientId":"${{ secrets.OIDC_SP2_CLIENT_ID }}","clientSecret":"${{ secrets.SP2_CLIENT_SECRET }}","tenantId":"${{ secrets.OIDC_SP2_TENANT_ID }}"}' - enable-AzPSSession: true - - - name: Check Last step failed - if: steps.login_15.outcome == 'success' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Last action should fail but not. Please check it.') diff --git a/.github/workflows/azure-login-positive.yml b/.github/workflows/azure-login-positive.yml deleted file mode 100644 index 16d15019c..000000000 --- a/.github/workflows/azure-login-positive.yml +++ /dev/null @@ -1,315 +0,0 @@ -name: Azure Login Action Positive Test -on: - workflow_dispatch: - push: - -permissions: - id-token: write - contents: read - -jobs: - - BasicTest: - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - environment: Automation test - - steps: - - name: 'Checking out repo code' - uses: actions/checkout@v4 - - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: 'Validate build' - run: | - npm install - npm run build - - - name: 'Run L0 tests' - run: | - npm run test - - - name: Login with creds - uses: ./ - with: - creds: ${{secrets.SP1}} - enable-AzPSSession: true - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Run Azure PowerShell - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Login with individual parameters - uses: ./ - with: - client-id: ${{ secrets.SP1_CLIENT_ID }} - tenant-id: ${{ secrets.SP1_TENANT_ID }} - subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} - enable-AzPSSession: true - - - name: Run Azure Cli again - run: | - az account show --output none - - - name: Run Azure PowerShell again - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Login with explicit auth-type - uses: ./ - with: - creds: ${{secrets.SP1}} - auth-type: SERVICE_PRINCIPAL - enable-AzPSSession: true - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Run Azure PowerShell - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - ParameterTest: - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - environment: Automation test - - steps: - - name: 'Checking out repo code' - uses: actions/checkout@v4 - - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: 'Validate build' - run: | - npm install - npm run build - - - name: Login with creds, disable ps session - uses: ./ - with: - creds: ${{secrets.SP1}} - enable-AzPSSession: false - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Login with creds, wrong boolean value - uses: ./ - with: - creds: ${{secrets.SP1}} - enable-AzPSSession: notboolean - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Login with creds, allow no subscription - uses: ./ - with: - creds: ${{secrets.SP1}} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Run Azure Cli - run: | - az account show --output none - az group show --name GitHubAction_CI_RG --output none - az vm list --output none - - - name: Run Azure PowerShell - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Login with individual parameters, allow no subscription - uses: ./ - with: - client-id: ${{ secrets.SP1_CLIENT_ID }} - tenant-id: ${{ secrets.SP1_TENANT_ID}} - subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Run Azure Cli again - run: | - az account show --output none - - - name: Run Azure PowerShell again - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Login with individual parameters, no subscription, allow no subscription - uses: ./ - with: - client-id: ${{ secrets.OIDC_SP2_CLIENT_ID }} - tenant-id: ${{ secrets.OIDC_SP2_TENANT_ID }} - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Run Azure Cli - shell: pwsh - run: | - $checkResult = (az account list --output json | ConvertFrom-Json).Count -eq 3 - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Run Azure PowerShell - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - - name: Login with creds, no subscription, allow no subscription - uses: ./ - with: - creds: '{"clientId":"${{ secrets.OIDC_SP2_CLIENT_ID }}","clientSecret":"${{ secrets.SP2_CLIENT_SECRET }}","tenantId":"${{ secrets.OIDC_SP2_TENANT_ID }}"}' - allow-no-subscriptions: true - enable-AzPSSession: true - - - name: Run Azure Cli - run: | - az account show --output none - - - name: Run Azure PowerShell - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = (Get-AzContext).Environment.Name -eq 'AzureCloud' - if(-not $checkResult){ - throw "Not all checks passed!" - } - - InDockerTest: - runs-on: ubuntu-latest - container: ubuntu:24.04 - environment: Automation test - steps: - - name: 'Checking out repo code' - uses: actions/checkout@v4 - - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: Install Azure CLI - run: | - apt-get update - apt-get install -y curl - curl -sL https://aka.ms/InstallAzureCLIDeb | bash - - - name: Check Azure CLI Version - run: | - az --version - - - name: Install Powershell - run: | - apt-get update - apt-get install -y wget - wget https://ftp.debian.org/debian/pool/main/i/icu/libicu72_72.1-3_amd64.deb - dpkg -i libicu72_72.1-3_amd64.deb - wget https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/powershell_7.5.0-1.deb_amd64.deb - dpkg -i powershell_7.5.0-1.deb_amd64.deb - - - name: Check Powershell Version - shell: pwsh - run: | - $PSVersionTable - - - name: Install Azure Powershell - shell: pwsh - run: | - Install-Module -Name Az -Repository PSGallery -Force - - - name: Check Azure Powershell Version - shell: pwsh - run: | - Get-Module -ListAvailable Az - - - name: 'Validate build' - run: | - npm install - npm run build - - - name: 'Run L0 tests' - run: | - npm run test - - - name: Login with individual parameters - uses: ./ - with: - client-id: ${{ secrets.SP1_CLIENT_ID }} - tenant-id: ${{ secrets.SP1_TENANT_ID }} - subscription-id: ${{ secrets.SP1_SUBSCRIPTION_ID }} - enable-AzPSSession: true - - - name: Run Azure Cli again - run: | - az group list --output none - - - name: Run Azure PowerShell again - uses: azure/powershell@v2 - with: - azPSVersion: "latest" - inlineScript: | - $checkResult = Get-AzResourceGroup diff --git a/.github/workflows/azure-login-pr-check.yml b/.github/workflows/azure-login-pr-check.yml deleted file mode 100644 index 279cf3cd7..000000000 --- a/.github/workflows/azure-login-pr-check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: pr-check - -on: - pull_request: - branches: - - master - - 'releases/*' -jobs: - az-login-test: - runs-on: windows-latest - steps: - - name: Checkout from PR branch - uses: actions/checkout@v4 - with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} - - # Using 20.x version as an example - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: installing node_modules - run: npm install - - - name: Build GitHub Action - run: npm run build - - - name: Run mock test - run: npm run test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45da7c8a3..f128428c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,12 +18,12 @@ jobs: steps: - name: 'Checking out repo code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - name: Set Node.js 20.x for GitHub Action - uses: actions/setup-node@v4 + - name: Set Node.js 24.x for GitHub Action + uses: actions/setup-node@v6 with: - node-version: 20.x + node-version: 24.x - name: 'Validate build' run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ca4c475ec..0ccb3a2ff 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 76c8789ec..5c13f1674 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Use Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - node-version: 20.x + node-version: 24.x - name: Run Markdownlint run: | npm i -g markdownlint-cli2 diff --git a/.gitignore b/.gitignore index 18e337ddd..fef07358f 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,12 @@ typings/ .DS_Store Thumbs.db +# IDE / editor +.vs/ +.vscode/ +.idea/ +*.swp + # Ignore built ts files __tests__/runner/* lib/**/* \ No newline at end of file diff --git a/README.md b/README.md index 615c032f3..5d3085cfb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ # Azure Login Action - [Azure Login Action](#azure-login-action) + - [Supported Versions](#supported-versions) + - [Version Selection](#version-selection) + - [Major-version tag](#major-version-tag) + - [Exact version tag](#exact-version-tag) + - [Branch reference](#branch-reference) + - [Commit SHA](#commit-sha) + - [Security Updates](#security-updates) - [Input Parameters](#input-parameters) - [`client-id`](#client-id) - [`subscription-id`](#subscription-id) @@ -48,6 +55,70 @@ Azure Login Action supports different ways of authentication with Azure. > [!WARNING] > Avoid using managed identity login on self-hosted runners in public repositories. Managed identities enable secure authentication with Azure resources and obtain Microsoft Entra ID tokens without the need for explicit credential management. Any user can open pull requests against your repository and access your self-hosted runners without credentials. See more details in [self-hosted runner security](https://docs.github.com/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security). +## Supported Versions + +Azure Login follows a major-version support model. + +| Version | Status | +| --- | --- | +| v3 | Supported | +| v2 | Maintenance mode (security fixes only) | +| v1 | End of Life (EOL) | + +New features are released only to supported versions. Security fixes are released to supported versions and versions in maintenance mode. + +Customers are strongly encouraged to use the latest v3 release. + +## Version Selection + +GitHub Actions users can reference Azure Login using several forms. + +### Major-version tag + +```yaml +uses: azure/login@v3 +``` + +Receives compatible updates, including security fixes, released to the referenced major version. + +### Exact version tag + +```yaml +uses: azure/login@v2.4.0 +``` + +Remains pinned to that specific release and does not automatically receive future fixes or updates. + +### Branch reference + +```yaml +uses: azure/login@master +``` + +Receives updates from the referenced branch. For stable workflows, use a supported major-version tag or pin to a full-length commit SHA. + +### Commit SHA + +```yaml +uses: azure/login@ +``` + +Remains pinned to that commit and does not automatically receive future fixes or updates. + +## Security Updates + +Security fixes are released to supported versions and versions in maintenance mode. Customers using exact version tags or commit SHA references must explicitly upgrade to a patched release to receive security fixes. + +```yaml +# Automatically receives future v3 security updates +uses: azure/login@v3 + +# Does not automatically receive future updates +uses: azure/login@v2.4.0 +``` + +Customers using v1 should migrate to v3. End-of-life releases no longer receive updates or security fixes. + ## Input Parameters |Parameter Name|Required?|Type|Default Value|Description| @@ -193,7 +264,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Azure login - uses: azure/login@v2 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -223,7 +294,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Azure login - uses: azure/login@v2 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -238,7 +309,7 @@ jobs: az account show - name: Azure PowerShell script - uses: azure/powershell@v2 + uses: azure/powershell@v3 with: azPSVersion: "latest" inlineScript: | @@ -285,7 +356,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: azure/login@v2 + - uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} @@ -312,7 +383,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: azure/login@v2 + - uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true @@ -325,7 +396,7 @@ jobs: az account show - name: Azure PowerShell script - uses: azure/powershell@v2 + uses: azure/powershell@v3 with: azPSVersion: "latest" inlineScript: | @@ -335,7 +406,7 @@ jobs: If you want to pass subscription ID, tenant ID, client ID, and client secret as individual parameters instead of bundling them in a single JSON object to address the [security concerns](https://docs.github.com/actions/security-guides/encrypted-secrets), below snippet can help with the same. ```yaml - - uses: azure/login@v2 + - uses: azure/login@v3 with: creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}' ``` @@ -379,7 +450,7 @@ jobs: runs-on: self-hosted steps: - name: Azure login - uses: azure/login@v2 + uses: azure/login@v3 with: auth-type: IDENTITY tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -396,7 +467,7 @@ jobs: az account show - name: Azure PowerShell script - uses: azure/powershell@v2 + uses: azure/powershell@v3 with: azPSVersion: "latest" inlineScript: | @@ -445,7 +516,7 @@ jobs: runs-on: self-hosted steps: - name: Azure login - uses: azure/login@v2 + uses: azure/login@v3 with: auth-type: IDENTITY client-id: ${{ secrets.AZURE_CLIENT_ID }} @@ -463,7 +534,7 @@ jobs: az account show - name: Azure PowerShell script - uses: azure/powershell@v2 + uses: azure/powershell@v3 with: azPSVersion: "latest" inlineScript: | @@ -485,7 +556,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: azure/login@v2 + - uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} environment: 'AzureUSGovernment' @@ -507,7 +578,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: azure/login@v2 + - uses: azure/login@v3 with: creds: ${{ secrets.AZURE_CREDENTIALS }} environment: 'AzureStack' @@ -534,7 +605,7 @@ jobs: steps: - name: Azure Login - uses: azure/login@v2 + uses: azure/login@v3 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -549,7 +620,7 @@ jobs: az account show - name: Run Azure PowerShell - uses: azure/powershell@v2 + uses: azure/powershell@v3 with: azPSVersion: "latest" inlineScript: | @@ -597,7 +668,7 @@ jobs: # enable cleanup for the 1st Azure Login - name: Azure Login - uses: azure/login@v2 + uses: azure/login@v3 env: AZURE_LOGIN_PRE_CLEANUP: true AZURE_LOGIN_POST_CLEANUP: true @@ -611,7 +682,7 @@ jobs: # disable cleanup for all other Azure Login - name: Azure Login 2 - uses: azure/login@v2 + uses: azure/login@v3 env: AZURE_LOGIN_PRE_CLEANUP: false AZURE_LOGIN_POST_CLEANUP: false @@ -625,7 +696,7 @@ jobs: # disable cleanup for all other Azure Login - name: Azure Login 3 - uses: azure/login@v2 + uses: azure/login@v3 env: AZURE_LOGIN_PRE_CLEANUP: false AZURE_LOGIN_POST_CLEANUP: false @@ -652,7 +723,7 @@ jobs: steps: - name: Azure Login - uses: azure/login@v2 + uses: azure/login@v3 env: AZURE_LOGIN_PRE_CLEANUP: ${{ startsWith(runner.name, 'GitHub Actions') }} AZURE_LOGIN_POST_CLEANUP: ${{ startsWith(runner.name, 'GitHub Actions') }} diff --git a/__tests__/PowerShell/AzPSScriptBuilder.test.ts b/__tests__/PowerShell/AzPSScriptBuilder.test.ts index 862d7a0f5..76b32c244 100644 --- a/__tests__/PowerShell/AzPSScriptBuilder.test.ts +++ b/__tests__/PowerShell/AzPSScriptBuilder.test.ts @@ -150,4 +150,157 @@ describe("Getting AzLogin PS script", () => { }); }); + const INJECT_RAW = "abc' ; Start-Process calc ; $x='"; + const INJECT_ESCAPED = "abc'' ; Start-Process calc ; $x=''"; + + test('SECURITY: tenant-id single quote is escaped (SP+secret path)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'true'); + setEnv('auth-type', 'SERVICE_PRINCIPAL'); + let creds = { + 'clientId': 'client-id', + 'clientSecret': 'client-secret', + 'tenantId': INJECT_RAW, + 'subscriptionId': 'subscription-id' + } + setEnv('creds', JSON.stringify(creds)); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-Tenant '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-Tenant '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: subscription-id single quote is escaped (SP+secret path)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'true'); + setEnv('auth-type', 'SERVICE_PRINCIPAL'); + let creds = { + 'clientId': 'client-id', + 'clientSecret': 'client-secret', + 'tenantId': 'tenant-id', + 'subscriptionId': INJECT_RAW + } + setEnv('creds', JSON.stringify(creds)); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-Subscription '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-Subscription '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: client-id single quote is escaped (SP+secret path, PSCredential)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'true'); + setEnv('auth-type', 'SERVICE_PRINCIPAL'); + let creds = { + 'clientId': INJECT_RAW, + 'clientSecret': 'client-secret', + 'tenantId': 'tenant-id', + 'subscriptionId': 'subscription-id' + } + setEnv('creds', JSON.stringify(creds)); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`New-Object System.Management.Automation.PSCredential('${INJECT_ESCAPED}',`); + expect(loginScript).not.toContain(`New-Object System.Management.Automation.PSCredential('${INJECT_RAW}',`); + }); + }); + + test('SECURITY: client-id single quote is escaped (OIDC path)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'false'); + setEnv('tenant-id', 'tenant-id'); + setEnv('subscription-id', 'subscription-id'); + setEnv('client-id', INJECT_RAW); + setEnv('auth-type', 'SERVICE_PRINCIPAL'); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + jest.spyOn(loginConfig, 'getFederatedToken').mockImplementation(async () => { loginConfig.federatedToken = "fake-token"; }); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-ApplicationId '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-ApplicationId '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: client-id single quote is escaped (user-assigned MI path)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'true'); + setEnv('auth-type', 'IDENTITY'); + setEnv('client-id', INJECT_RAW); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-AccountId '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-AccountId '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: tenant-id and subscription-id single quotes are escaped (system-assigned MI path)', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'false'); + setEnv('tenant-id', INJECT_RAW); + setEnv('subscription-id', INJECT_RAW); + setEnv('auth-type', 'IDENTITY'); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-Tenant '${INJECT_ESCAPED}'`); + expect(loginScript).toContain(`-Subscription '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-Tenant '${INJECT_RAW}'`); + expect(loginScript).not.toContain(`-Subscription '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: resourceManagerEndpointUrl single quote is escaped (AzureStack path)', () => { + setEnv('environment', 'azurestack'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'true'); + setEnv('auth-type', 'SERVICE_PRINCIPAL'); + let creds = { + 'clientId': 'client-id', + 'clientSecret': 'client-secret', + 'tenantId': 'tenant-id', + 'subscriptionId': 'subscription-id', + 'resourceManagerEndpointUrl': INJECT_RAW + } + setEnv('creds', JSON.stringify(creds)); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([_, loginScript]) => { + expect(loginScript).toContain(`-ARMEndpoint '${INJECT_ESCAPED}'`); + expect(loginScript).not.toContain(`-ARMEndpoint '${INJECT_RAW}'`); + }); + }); + + test('SECURITY: escapePSSingleQuoted handles null/undefined without throwing', () => { + setEnv('environment', 'azurecloud'); + setEnv('enable-AzPSSession', 'true'); + setEnv('allow-no-subscriptions', 'false'); + setEnv('auth-type', 'IDENTITY'); + + let loginConfig = new LoginConfig(); + loginConfig.initialize(); + return AzPSSCriptBuilder.getAzPSLoginScript(loginConfig).then(([loginMethod, loginScript]) => { + expect(loginScript).toContain("Connect-AzAccount -Identity -Environment 'azurecloud'"); + expect(loginMethod).toBe('system-assigned managed identity'); + }); + }); + }); \ No newline at end of file diff --git a/action.yml b/action.yml index 44c1f66a6..bf52a453b 100644 --- a/action.yml +++ b/action.yml @@ -38,7 +38,7 @@ branding: icon: 'login.svg' color: 'blue' runs: - using: 'node20' + using: 'node24' main: 'lib/main/index.js' post-if: (!env.AZURE_LOGIN_POST_CLEANUP || env.AZURE_LOGIN_POST_CLEANUP != 'false') post: 'lib/cleanup/index.js' diff --git a/package-lock.json b/package-lock.json index d0ef42c4a..6e981114d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,20 @@ { "name": "login", - "version": "2.2.0", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "login", - "version": "2.2.0", "license": "MIT", "dependencies": { "@actions/core": "1.9.1", "@actions/exec": "^1.0.1", - "@actions/io": "^1.0.1", - "package-lock": "^1.0.3" + "@actions/io": "^1.0.1" }, "devDependencies": { "@types/jest": "^29.2.4", - "@types/node": "^20.11.1", + "@types/node": "^24.0.0", "@vercel/ncc": "^0.38.1", "jest": "^29.3.1", "jest-circus": "^29.3.1", @@ -77,73 +75,20 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { "version": "7.20.5", "dev": true, @@ -321,7 +266,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -329,7 +276,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -345,91 +294,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.20.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "@babel/types": "^7.29.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.9", - "dev": true, - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -601,13 +487,15 @@ } }, "node_modules/@babel/template": { - "version": "7.23.9", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -634,13 +522,14 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1077,11 +966,13 @@ } }, "node_modules/@types/node": { - "version": "20.11.19", + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/prettier": { @@ -1172,13 +1063,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/asyncc": { - "version": "2.0.6", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/babel-jest": { "version": "29.3.1", "dev": true, @@ -1267,10 +1151,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1451,15 +1339,9 @@ "dev": true, "license": "MIT" }, - "node_modules/commander": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/concat-map": { "version": "0.0.1", + "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { @@ -1468,7 +1350,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -1666,8 +1550,24 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", + "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "dev": true, @@ -1710,6 +1610,7 @@ }, "node_modules/glob": { "version": "7.2.3", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -1750,14 +1651,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -1799,6 +1692,7 @@ }, "node_modules/inflight": { "version": "1.0.6", + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -1807,6 +1701,7 @@ }, "node_modules/inherits": { "version": "2.0.4", + "dev": true, "license": "ISC" }, "node_modules/is-arrayish": { @@ -2482,11 +2377,15 @@ }, "node_modules/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -2556,10 +2455,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "license": "MIT" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "dev": true, @@ -2609,11 +2504,13 @@ "license": "MIT" }, "node_modules/micromatch": { - "version": "4.0.5", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2629,7 +2526,10 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -2679,6 +2579,7 @@ }, "node_modules/once": { "version": "1.4.0", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -2745,20 +2646,6 @@ "node": ">=6" } }, - "node_modules/package-lock": { - "version": "1.0.3", - "license": "Unlicense", - "dependencies": { - "asyncc": "^2.0.4", - "commander": "^5.0.0", - "glob": "^7.1.6", - "lodash": "^4.17.15", - "traverse": "^0.6.6" - }, - "bin": { - "package-lock": "bin/package-lock.js" - } - }, "node_modules/parse-json": { "version": "5.2.0", "dev": true, @@ -2786,6 +2673,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2805,7 +2693,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -3121,14 +3011,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3142,13 +3024,6 @@ "node": ">=8.0" } }, - "node_modules/traverse": { - "version": "0.6.7", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ts-jest": { "version": "29.0.3", "dev": true, @@ -3237,7 +3112,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -3324,6 +3201,7 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/package.json b/package.json index 1ebd3012a..6364c76cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "login", - "version": "2.2.0", "description": "Login Azure wraps the az login, allowing for Azure actions to log into Azure", "main": "lib/main/index.js", "scripts": { @@ -13,7 +12,7 @@ "license": "MIT", "devDependencies": { "@types/jest": "^29.2.4", - "@types/node": "^20.11.1", + "@types/node": "^24.0.0", "@vercel/ncc": "^0.38.1", "jest": "^29.3.1", "jest-circus": "^29.3.1", @@ -23,7 +22,6 @@ "dependencies": { "@actions/core": "1.9.1", "@actions/exec": "^1.0.1", - "@actions/io": "^1.0.1", - "package-lock": "^1.0.3" + "@actions/io": "^1.0.1" } } diff --git a/src/PowerShell/AzPSScriptBuilder.ts b/src/PowerShell/AzPSScriptBuilder.ts index 5cd34580f..8b6b05824 100644 --- a/src/PowerShell/AzPSScriptBuilder.ts +++ b/src/PowerShell/AzPSScriptBuilder.ts @@ -21,12 +21,20 @@ export default class AzPSScriptBuilder { return script; } + // Doubles single quotes for safe interpolation into a PowerShell '...' literal. + private static escapePSSingleQuoted(value: string): string { + if (value === null || value === undefined) { + return ""; + } + return String(value).split("'").join("''"); + } + static async getAzPSLoginScript(loginConfig: LoginConfig) { let loginMethodName = ""; let commands = ""; if (loginConfig.environment.toLowerCase() == "azurestack") { - commands += `Add-AzEnvironment -Name '${loginConfig.environment}' -ARMEndpoint '${loginConfig.resourceManagerEndpointUrl}' | out-null;`; + commands += `Add-AzEnvironment -Name '${loginConfig.environment}' -ARMEndpoint '${AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.resourceManagerEndpointUrl)}' | out-null;`; } if (loginConfig.authType === LoginConfig.AUTH_TYPE_SERVICE_PRINCIPAL) { if (loginConfig.servicePrincipalSecret) { @@ -64,10 +72,11 @@ export default class AzPSScriptBuilder { } private static loginWithSecret(loginConfig: LoginConfig): string { - let servicePrincipalSecret: string = loginConfig.servicePrincipalSecret.split("'").join("''"); + let servicePrincipalSecret: string = AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.servicePrincipalSecret); + let servicePrincipalId: string = AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.servicePrincipalId); let loginCmdlet = `$psLoginSecrets = ConvertTo-SecureString '${servicePrincipalSecret}' -AsPlainText -Force; `; - loginCmdlet += `$psLoginCredential = New-Object System.Management.Automation.PSCredential('${loginConfig.servicePrincipalId}', $psLoginSecrets); `; - + loginCmdlet += `$psLoginCredential = New-Object System.Management.Automation.PSCredential('${servicePrincipalId}', $psLoginSecrets); `; + let cmdletSuffix = "-Credential $psLoginCredential"; loginCmdlet += AzPSScriptBuilder.psLoginCmdlet(loginConfig.authType, loginConfig.environment, loginConfig.tenantId, loginConfig.subscriptionId, cmdletSuffix); @@ -76,7 +85,9 @@ export default class AzPSScriptBuilder { private static async loginWithOIDC(loginConfig: LoginConfig) { await loginConfig.getFederatedToken(); - let cmdletSuffix = `-ApplicationId '${loginConfig.servicePrincipalId}' -FederatedToken '${loginConfig.federatedToken}'`; + let servicePrincipalId: string = AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.servicePrincipalId); + let federatedToken: string = AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.federatedToken); + let cmdletSuffix = `-ApplicationId '${servicePrincipalId}' -FederatedToken '${federatedToken}'`; return AzPSScriptBuilder.psLoginCmdlet(loginConfig.authType, loginConfig.environment, loginConfig.tenantId, loginConfig.subscriptionId, cmdletSuffix); } @@ -86,7 +97,8 @@ export default class AzPSScriptBuilder { } static loginWithUserAssignedIdentity(loginConfig: LoginConfig): string { - let cmdletSuffix = `-AccountId '${loginConfig.servicePrincipalId}'`; + let servicePrincipalId: string = AzPSScriptBuilder.escapePSSingleQuoted(loginConfig.servicePrincipalId); + let cmdletSuffix = `-AccountId '${servicePrincipalId}'`; return AzPSScriptBuilder.psLoginCmdlet(loginConfig.authType, loginConfig.environment, loginConfig.tenantId, loginConfig.subscriptionId, cmdletSuffix); } @@ -99,10 +111,10 @@ export default class AzPSScriptBuilder { } loginCmdlet += `-Environment '${environment}' `; if(tenantId){ - loginCmdlet += `-Tenant '${tenantId}' `; + loginCmdlet += `-Tenant '${AzPSScriptBuilder.escapePSSingleQuoted(tenantId)}' `; } if(subscriptionId){ - loginCmdlet += `-Subscription '${subscriptionId}' `; + loginCmdlet += `-Subscription '${AzPSScriptBuilder.escapePSSingleQuoted(subscriptionId)}' `; } loginCmdlet += `${cmdletSuffix} -InformationAction Ignore | out-null;`; return loginCmdlet; diff --git a/src/common/Utils.ts b/src/common/Utils.ts index 8e9186d32..ecd45df94 100644 --- a/src/common/Utils.ts +++ b/src/common/Utils.ts @@ -7,8 +7,9 @@ import { AzPSConstants, AzPSUtils } from '../PowerShell/AzPSUtils'; export function setUserAgent(): void { let usrAgentRepo = crypto.createHash('sha256').update(`${process.env.GITHUB_REPOSITORY}`).digest('hex'); let actionName = 'AzureLogin'; - process.env.AZURE_HTTP_USER_AGENT = (!!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT} ` : '') + `GITHUBACTIONS/${actionName}@v2_${usrAgentRepo}_${process.env.RUNNER_ENVIRONMENT}_${process.env.GITHUB_RUN_ID}`; - process.env.AZUREPS_HOST_ENVIRONMENT = (!!process.env.AZUREPS_HOST_ENVIRONMENT ? `${process.env.AZUREPS_HOST_ENVIRONMENT} ` : '') + `GITHUBACTIONS/${actionName}@v2_${usrAgentRepo}_${process.env.RUNNER_ENVIRONMENT}_${process.env.GITHUB_RUN_ID}`; + let actionRef = process.env.GITHUB_ACTION_REF || 'unknown'; + process.env.AZURE_HTTP_USER_AGENT = (!!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT} ` : '') + `GITHUBACTIONS/${actionName}@${actionRef}_${usrAgentRepo}_${process.env.RUNNER_ENVIRONMENT}_${process.env.GITHUB_RUN_ID}`; + process.env.AZUREPS_HOST_ENVIRONMENT = (!!process.env.AZUREPS_HOST_ENVIRONMENT ? `${process.env.AZUREPS_HOST_ENVIRONMENT} ` : '') + `GITHUBACTIONS/${actionName}@${actionRef}_${usrAgentRepo}_${process.env.RUNNER_ENVIRONMENT}_${process.env.GITHUB_RUN_ID}`; } export async function cleanupAzCLIAccounts(): Promise {