You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
12.[Scaling to 200 Projects with a Custom Pipeline Task](#12-scaling-to-200-projects-with-a-custom-pipeline-task)
29
-
13.[References](#13-references)
29
+
13.[Pipeline Decorators — The Hardest Scenario](#13-pipeline-decorators--the-hardest-scenario)
30
+
14.[Why Microsoft Removed Build Identity Access — Security Analysis](#14-why-microsoft-removed-build-identity-access--security-analysis)
31
+
15.[References](#15-references)
30
32
31
33
---
32
34
@@ -705,7 +707,327 @@ foreach ($project in $projects) {
705
707
706
708
---
707
709
708
-
## 13. References
710
+
## 13. Pipeline Decorators — The Hardest Scenario
711
+
712
+
### Context
713
+
714
+
The customer uses a **pipeline decorator** (not a custom task) that injects steps into every pipeline in the organization. The decorator currently uses `System.AccessToken` to call Advanced Security APIs and gate pipelines. After April 15, 2026, this will stop working.
715
+
716
+
Key constraint: the customer already has **200 service connections with SPs** to cloud environments — but those are **Azure Resource Manager** SPs for deploying to Azure, not ADO-scoped SPs. Adding all 200 as Basic users in ADO would cost ~$1,200/month and is unnecessary.
717
+
718
+
---
719
+
720
+
### Why This Is Harder Than a Custom Task
721
+
722
+
Pipeline decorators have **unique constraints** that don't apply to regular pipeline tasks:
723
+
724
+
| Constraint | Impact |
725
+
|-----------|--------|
726
+
|**Service connection names must be hardcoded** — no variables, no parameters, no runtime expressions | The SC name in the decorator YAML must be a literal string, resolved at compile time |
727
+
|**Decorator runs on EVERY pipeline in the org**| The referenced SC must be authorized in every project where pipelines run |
728
+
|**Decorators cannot accept user inputs**| Unlike custom tasks, there's no `task.json` with input fields — the decorator YAML is fixed |
729
+
|**SC authorization is project-scoped**| Even with "Grant access to all pipelines," that only applies within the project where the SC lives |
730
+
731
+
This means: **the decorator must reference a single, hardcoded service connection name, and that SC must exist and be authorized in every project.**
732
+
733
+
---
734
+
735
+
### Evaluating Your Proposed Solution
736
+
737
+
#### Your Solution: One Shared WIF Service Connection Across All Projects
738
+
739
+
**Proposal**: Create **1 Service Principal** with only `Advanced Security: Read alerts` at the org level, create **1 Azure DevOps Service Connection** with Workload Identity Federation, and share it across all 200 projects with a consistent name (e.g., `advsec-gate-sc`).
740
+
741
+
**Verdict: This is a sound approach.** Here's the analysis:
742
+
743
+
| Aspect | Assessment |
744
+
|--------|-----------|
745
+
|**Security**| ✅ The SP has the narrowest possible scope — only `Advanced Security: Read alerts`, org-wide. It cannot modify code, manage releases, access ARM resources, or change alert states. |
746
+
|**Cost**| ✅ Only 1 Basic license (~$6/month). No Advanced Security committer license since the SP doesn't commit code. |
747
+
|**Shared SC concern**| ⚠️ Microsoft recommends against sharing SCs broadly, but that guidance targets **ARM SCs with cloud access**. This SC can only read security alerts in ADO — the blast radius of compromise is limited to reading vulnerability data (not modifying anything). |
748
+
|**Decorator compatibility**| ✅ The SC name can be hardcoded in the decorator YAML since it's the same name everywhere. |
749
+
|**Rollout**| 🟡 Must create/share the SC in all 200 projects (scriptable via REST API). |
750
+
751
+
**Key defense of this approach**: The reason Microsoft discourages shared SCs is the principle of least privilege — a shared ARM SC could allow pipelines in Project A to deploy to Project B's Azure resources. But this SC has **zero ARM access**. It can only read ADO security alerts. The risk profile is fundamentally different.
752
+
753
+
---
754
+
755
+
### Challenging Your Solution — Risks to Consider
756
+
757
+
| Risk | Severity | Mitigation |
758
+
|------|----------|------------|
759
+
|**Alert data exposure** — any pipeline in the org can read security alerts from any repo | 🟡 Medium | Accept if org policy allows central security visibility; if not, scope the SP's `Read alerts` permission per-project instead of org-wide |
760
+
|**SC authorization sprawl** — must grant "Use" to all pipelines in 200 projects | 🟡 Medium | Script it via REST API; use "Grant access to all pipelines" per project |
761
+
|**Single point of failure** — if the SC breaks, all 200 projects lose their gate | 🟡 Medium | Monitor the SC, set up alerts; WIF has no secrets to expire |
762
+
|**Decorator update required** — decorator YAML must be changed from `System.AccessToken` to `AzureCLI@3` with the SC | 🟢 Low | One-time change, centrally managed |
763
+
|**SC doesn't exist yet in new projects** — new projects need manual/automated provisioning | 🟡 Medium | Automate with a project creation hook or periodic script |
764
+
765
+
---
766
+
767
+
### Alternative Solutions Compared
768
+
769
+
#### Alternative 1: Decorator Uses System.AccessToken + Explicitly Grant "Advanced Security: Read alerts" to Build Service
770
+
771
+
**Status**: ❌ **Will NOT work after April 15, 2026.** Microsoft is blocking build service identities at the API level regardless of explicit permissions. This is not a permission issue — it's an identity-type block.
| Decorator | ❌ Decorator can't dynamically select which SC to use — it must be hardcoded |
781
+
782
+
**Verdict**: Not viable. The decorator's hardcoded SC name means you NEED one shared SC anyway. Multiple SPs defeats the purpose and costs 200× more.
783
+
784
+
#### Alternative 3: Status Checks (Sprint 271+) — Eliminate the Decorator
785
+
786
+
| Aspect | Assessment |
787
+
|--------|-----------|
788
+
| Cost | ✅ $0 |
789
+
| Security | ✅ Native platform feature |
790
+
| Rollout | 🟡 Script branch policies across all repos via REST API |
791
+
| Flexibility | ❌ PR gating only — no deployment gate, no custom logic |
792
+
| Decorator | N/A — replaces the decorator entirely |
793
+
794
+
**Verdict**: Best for PR gating. But if the decorator does more than just pass/fail (custom severity thresholds, alert categorization, deployment gates, reporting), Status Checks won't replace it.
795
+
796
+
#### Alternative 4: Decorator Acquires Token from Key Vault (No Service Connection)
797
+
798
+
The decorator injects a script step that reads SP credentials from an Azure Key Vault-linked variable group, then acquires an Entra token directly. This avoids the SC-in-decorator limitation.
799
+
800
+
```yaml
801
+
# decorator.yml
802
+
steps:
803
+
- ${{ if ne(variables['skipAdvSecGate'], 'true') }}:
804
+
- task: AzureKeyVault@2
805
+
inputs:
806
+
azureSubscription: 'keyvault-reader-sc'# ARM SC - already exists
**Your intuition is correct.** Prior to Sprint 269, any pipeline running with `System.AccessToken` could read Advanced Security alerts because the Build Service identity had **implicit API access by default**. This created a real security risk:
988
+
989
+
```
990
+
Risk Chain:
991
+
Any pipeline task/extension (including marketplace)
992
+
→ Accesses System.AccessToken (tasks get it automatically)
993
+
→ Authenticates as Build Service identity
994
+
→ Calls Advanced Security API (allowed by default pre-Sprint 269)
995
+
→ Reads all security alerts for the project/repo
996
+
→ Can exfiltrate vulnerability data
997
+
```
998
+
999
+
### Key Facts from Microsoft Documentation
1000
+
1001
+
1.**Build Service had default access**: The `Project Collection Build Service` and project-scoped Build Service identities were allowed to call Advanced Security APIs by default — no explicit permission grant was needed.
-**Pipeline Tasks/Extensions**: can access the job access token as part of their normal operation via the Task SDK, without the user explicitly mapping it
1006
+
1007
+
3.**Shared identity problem**: The Build Service identity is **shared across ALL pipelines in a project** (or the entire collection if not restricted). There's no per-pipeline isolation. Any task in any pipeline gets the same identity.
> *"This change prevents pipeline-based automation from accessing or modifying security alert data using build service accounts, reducing the risk of unintended alert state changes during CI/CD runs."*
1011
+
1012
+
5.**The fix**: Microsoft is requiring a **named service principal** with explicit `Advanced Security: Read alerts` permission, which provides:
1013
+
- Explicit, auditable identity (not a shared build account)
- Per-pipeline access control via service connections
1017
+
1018
+
### Is a Simple Extension a Risk?
1019
+
1020
+
**Yes.** A marketplace extension (or any custom task) installed in the organization:
1021
+
- Runs with the job's access token automatically (tasks access it via the Task SDK)
1022
+
- Does NOT need "Allow scripts to access the OAuth token" — that setting only applies to script steps, not task steps
1023
+
- Could call any API the Build Service identity has access to
1024
+
- Before Sprint 269, that included Advanced Security APIs
1025
+
1026
+
**This is precisely why Microsoft made the Sprint 269 change** — to ensure that only explicitly authorized service principals (with audit trails and conditional access) can read security alert data, not any pipeline task running under the broadly-shared Build Service identity.
1027
+
1028
+
---
1029
+
1030
+
## 15. References
709
1031
710
1032
| Resource | URL |
711
1033
|----------|-----|
@@ -719,6 +1041,12 @@ foreach ($project in $projects) {
719
1041
| Entra Tokens via Azure CLI |https://learn.microsoft.com/en-us/azure/devops/cli/entra-tokens|
720
1042
| No New Azure DevOps OAuth Apps (April 2025) |https://devblogs.microsoft.com/devops/no-new-azure-devops-oauth-apps/|
721
1043
| Configure Advanced Security Status Checks |https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features|
1044
+
| Author a Pipeline Decorator |https://learn.microsoft.com/en-us/azure/devops/extend/develop/add-pipeline-decorator|
0 commit comments