-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathauthentication-test.yml
More file actions
219 lines (205 loc) · 11.3 KB
/
Copy pathauthentication-test.yml
File metadata and controls
219 lines (205 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# AZLOCAL-PIPELINE-ID: authentication-test
# Step.00 - Authentication Validation and Subscription Scope Report
#
# PURPOSE:
# Validates that the App Registration / federated credentials / GitHub secrets / RBAC
# role assignments all line up, AND produces a Subscription Scope report that lists every
# subscription the pipeline identity can see. Run this:
# - BEFORE adding the seven real workflows (inventory, tags, readiness, apply, status,
# fleet-status, fleet-health). It narrows any failure to one small workflow rather
# than seven interacting workflows.
# - PERIODICALLY (recommended monthly, or after every RBAC change in the tenant) to
# confirm the pipeline identity's subscription scope has not silently widened or
# narrowed - new subscriptions get onboarded, MG-level role assignments inherit,
# leavers / re-orgs change tenant scope. A drift in this report is the earliest
# signal that downstream fleet reports are about to under- or over-count clusters.
#
# WHAT THIS PIPELINE PROBES:
# 1. azure/login OIDC token exchange (App Registration + federated credential).
# 2. Repository / environment secrets (AZURE_CLIENT_ID, _TENANT_ID, _SUBSCRIPTION_ID).
# 3. Azure RBAC on the App Registration's service principal.
# 4. Resource Graph reachability (proves the SP can actually see clusters).
# 5. Subscription scope: total count and full Subscription ID / Name table
# visible to the pipeline identity.
#
# REPORTS GENERATED:
# - JUnit XML: One test suite for Authentication, one for Subscription Scope (one testcase
# per subscription), one for Resource Graph reachability. Renders in the GitHub Checks
# UI via dorny/test-reporter.
# - GITHUB_STEP_SUMMARY markdown: Summary header (Authentication: working / Count of
# subscriptions = XXX) followed by a sortable Subscription ID + Name table.
# - Artifact (auth-report.zip): JUnit XML + subscriptions.json + subscriptions.csv for
# downstream consumption.
#
# HOW TO TRIGGER (after the file is committed to the default branch):
# # Branch-scoped run - exercises the 'GitHubActions-main' federated credential.
# gh workflow run authentication-test.yml --repo <owner>/<repo>
#
# # Environment-scoped run - exercises the env-scoped federated credentials.
# gh workflow run authentication-test.yml --repo <owner>/<repo> -f environment=DevTest
#
# # Watch the most recent run live.
# gh run watch --repo <owner>/<repo>
#
# See Automation-Pipeline-Examples/README.md, section 5.1, for the full setup story.
# Workflow name carries the same Step.N - prefix as the filename so the GitHub
# Actions sidebar (which sorts workflows alphabetically by this `name:` field)
# lists the eight pipelines in execution order.
name: Step.00 - Authentication Validation and Subscription Scope Report
on:
# BEGIN-AZLOCAL-CUSTOMIZE:schedule-triggers
# Add `schedule:` cron blocks here to enable automated runs, e.g.:
# schedule:
# - cron: '0 6 * * 1' # Mondays at 06:00 UTC
# Content between BEGIN/END markers is preserved by
# Update-AzLocalPipelineExample across module upgrades.
# END-AZLOCAL-CUSTOMIZE:schedule-triggers
workflow_dispatch:
inputs:
environment:
description: 'GitHub environment to test (leave blank to test the branch-scoped federated credential).'
required: false
type: choice
default: ''
options:
- ''
- DevTest
- PreProduction
- Production
module_version:
description: 'Pin AzLocal.UpdateManagement version (empty = latest from PSGallery). See Automation-Pipeline-Examples/README.md section 5 "Optional configuration".'
required: false
default: ''
env:
# Module version this workflow YAML was generated against. The install step compares
# this to the version actually installed and to the latest on PSGallery, and emits a
# ::notice annotation if the YAML appears stale - prompting you to refresh via
# Copy-AzLocalPipelineExample. See Automation-Pipeline-Examples/README.md section 5.
GENERATED_AGAINST_MODULE_VERSION: '0.8.75'
# Resolution order for the module version pin (leave all unset to install the latest,
# which is the default "fix-forward" behaviour): manual workflow_dispatch input >
# repository variable 'REQUIRED_MODULE_VERSION' > empty (latest).
REQUIRED_MODULE_VERSION: ${{ github.event.inputs.module_version || vars.REQUIRED_MODULE_VERSION || '' }}
# v0.8.4 - opt this workflow into Node.js 24 for all JavaScript actions
# (actions/checkout, actions/download-artifact, actions/upload-artifact,
# azure/login, dorny/test-reporter, etc). Per GitHub's 2025-09-19 deprecation
# notice Node 20 is forced off by default on 2026-06-16 and removed from the
# runner on 2026-09-16. Setting this env var silences the deprecation warnings
# and exercises Node 24 ahead of the cut-over. To temporarily opt back out,
# set ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true.
# https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
# OIDC token exchange requires id-token: write. contents: read is the GitHub default
# for workflow_dispatch and is harmless here; the validation run does not push commits.
# checks: write lets dorny/test-reporter publish the JUnit results as a Checks UI panel.
permissions:
id-token: write
contents: read
checks: write
jobs:
validate:
name: Validate OIDC + RBAC + Subscription Scope
runs-on: windows-latest
# When `environment` is blank, this resolves to no environment and uses the
# branch-scoped federated credential. When set, GitHub injects environment-scoped
# secrets (if any) and signs the OIDC token with `environment:<name>` in the sub claim.
environment: ${{ inputs.environment }}
steps:
# dorny/test-reporter@v3 (used at the end of this job) shells out to
# `git ls-files` to resolve the JUnit XML path against the repo's
# tracked files. Without a checkout, it fails with exit 128:
# fatal: not a git repository (or any of the parent directories): .git
# The checkout itself is otherwise unnecessary for Step.0 (the auth probe
# does not consume any repo content) but is required for the report step.
- name: Checkout repository
uses: actions/checkout@v5
- name: Azure login (OIDC)
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
# AZURE_SUBSCRIPTION_ID is a repository *Variable* (vars.*), not a Secret. It
# is consumed ONLY here: azure/login@v3 runs `az account set --subscription
# <id>` after the OIDC token exchange so the runner has a default
# `az account` context. It is NOT used to scope Azure Resource Graph queries
# (those run fleet-wide across every subscription the federated identity can
# read) and is NOT interpolated into Azure portal deep-link URLs (those use
# the per-row `subscriptionId` returned by ARG).
# Set it via: gh variable set AZURE_SUBSCRIPTION_ID --body <subId>
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- name: Install AzLocal.UpdateManagement from PSGallery
# Step.0 itself relies on the module (Export-AzLocalAuthValidationReport
# runs the four auth/RBAC/ARG probes); installing here also gives
# operators an early, consistent drift signal *before* the rest of
# the seven pipelines run. If the banner reports the YAML is stale
# or the installed module is older than expected, refresh the YAMLs
# via Copy-AzLocalPipelineExample -Update and re-run.
# v0.8.5 thin-YAML: drift detection + banner + step outputs are all
# produced by Add-AzLocalPipelineVersionBanner (Public cmdlet).
shell: pwsh
id: module-version
run: |
$ErrorActionPreference = 'Stop'
$installArgs = @{ Name = 'AzLocal.UpdateManagement'; Scope = 'CurrentUser'; Force = $true; AllowClobber = $true }
if ($env:REQUIRED_MODULE_VERSION) {
$installArgs.RequiredVersion = $env:REQUIRED_MODULE_VERSION
Write-Host "REQUIRED_MODULE_VERSION is set - pinning install to v$($env:REQUIRED_MODULE_VERSION)."
} else {
Write-Host "REQUIRED_MODULE_VERSION is empty - installing the latest version from PSGallery (default fix-forward behaviour)."
}
Install-Module @installArgs
Import-Module AzLocal.UpdateManagement -Force
Add-AzLocalPipelineVersionBanner `
-GeneratedAgainstVersion $env:GENERATED_AGAINST_MODULE_VERSION `
-PinnedVersion $env:REQUIRED_MODULE_VERSION
- name: Collect Authentication and Subscription Scope Report
id: report
shell: pwsh
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
INSTALLED_MODULE_VERSION: ${{ steps.module-version.outputs.installed_module_version }}
GENERATED_AGAINST_VERSION: ${{ steps.module-version.outputs.generated_against_version }}
LATEST_ON_PSGALLERY: ${{ steps.module-version.outputs.latest_on_psgallery }}
# v0.8.5 thin-YAML: the ~200-line inline run block (az probes,
# XML scaffolding, markdown summary, step-output writes) has been
# condensed into the Public cmdlet Export-AzLocalAuthValidationReport.
# The cmdlet performs all four probes (az account show / role
# assignment list / az account list / Resource Graph query),
# writes the JUnit XML + subscriptions.json + subscriptions.csv,
# emits the markdown step summary, and writes the three step
# outputs (subscription_count, cluster_count, auth_valid).
run: |
$ErrorActionPreference = 'Stop'
Import-Module AzLocal.UpdateManagement -Force
Export-AzLocalAuthValidationReport `
-AzureClientId $env:AZURE_CLIENT_ID `
-ReportDirectory './reports' `
-InstalledModuleVersion $env:INSTALLED_MODULE_VERSION `
-GeneratedAgainstVersion $env:GENERATED_AGAINST_VERSION `
-LatestOnPSGallery $env:LATEST_ON_PSGALLERY
- name: Compute Artifact Timestamp
id: artifact-stamp
shell: pwsh
# artifact convention: every downloadable artifact gets a UTC timestamp suffix so
# multiple runs on the same day produce distinct zip names.
run: |
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMdd_HHmmss')
"timestamp=$stamp" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
Write-Host "Artifact timestamp: $stamp"
- name: Upload auth report artifact
if: always()
uses: actions/upload-artifact@v6
with:
name: azlocal-step.0-auth-report_${{ steps.artifact-stamp.outputs.timestamp }}
path: ./reports/
retention-days: 30
- name: Publish JUnit Diagnostic Results
if: always()
uses: dorny/test-reporter@v3
with:
name: '[JUnit Debug] Authentication Validation and Subscription Scope Report'
path: ./reports/auth-report.xml
reporter: java-junit
fail-on-error: false
list-suites: failed
list-tests: failed