Domain 4: Develop a Security and Compliance Plan
Exam Weight: 10–15% 📁 ← Back to Home
📑 Table of Contents
- 4.1 Authentication and Authorization
- 4.2 Managing Sensitive Information in Automation
- 4.3 Automate Security and Compliance Scanning
4.1 Design and Implement Authentication and Authorization Methods
Service Principal vs Managed Identity
⭐ This is a very commonly tested comparison!
| Service Principal | Managed Identity | |
|---|---|---|
| What is it | App/service identity in Azure AD | Azure-managed identity for Azure resources |
| Credentials | Client ID + Secret or Certificate | No credentials to manage |
| Secret rotation | Manual (your responsibility) | Automatic (Azure handles it) |
| Best for | Cross-tenant, non-Azure resources, CI/CD external to Azure | Azure resources calling other Azure services |
| Types | Single type | System-assigned, User-assigned |
| Supports federated identity | ✅ (OIDC) | ✅ (system-assigned) |
System-assigned Managed Identity:
- Created with the Azure resource (VM, App Service, Function App, etc.)
- Lifecycle tied to the resource — deleted when resource is deleted
- One-to-one: one identity per resource
User-assigned Managed Identity:
- Created independently as a standalone Azure resource
- Can be assigned to multiple Azure resources
- Lifecycle managed independently — persists when resources are deleted
When to use User-assigned vs System-assigned: | Use Case | Recommendation | |———-|—————| | Multiple resources need same permissions | User-assigned (share one identity) | | Resource is unique and self-contained | System-assigned | | Pre-provision identity before resource creation | User-assigned | | Simplest setup for a single resource | System-assigned |
GitHub Authentication
GitHub Apps
- Recommended for automation and integrations
- Act as their own identity (not tied to a user)
- Fine-grained permissions at repo/org level
- OAuth 2.0 flow with JWT for auth
- Higher rate limits than PATs
GITHUB_TOKEN
- Automatically generated per-workflow-run by GitHub Actions
- Scoped to the current repository
- Permissions configurable in workflow YAML:
1 2 3 4
permissions: contents: read pull-requests: write issues: write
- Expires when workflow completes
- Best for: Actions that work within the same repo
Personal Access Tokens (PATs)
- Classic PAT: Scoped to user account, coarse permissions
- Fine-grained PAT: Scoped to specific repos, expiry date required
- Avoid where possible — tied to an individual, not a service
- Store as encrypted secrets, never in code
Choosing GitHub auth method:
1
2
3
4
Need to call GitHub API from Actions workflow?
├── Same repo → Use GITHUB_TOKEN (automatic, no setup)
├── Cross-repo, same org → Fine-grained PAT or GitHub App
└── Third-party integration → GitHub App (preferred)
Azure DevOps Authentication
Service Connections
- Store credentials for external services (Azure, AWS, Docker, SonarQube, etc.)
- Types: | Type | Use Case | |——|———| | Azure Resource Manager | Deploy to Azure subscriptions | | Docker Registry | Push images to ACR or Docker Hub | | Kubernetes | Deploy to AKS or any Kubernetes cluster | | Generic | REST APIs, custom services | | GitHub | Access GitHub repositories | | SSH | SSH-based deployments |
Creating Azure RM Service Connection:
- Recommended: Use Workload Identity Federation (no secret/certificate needed)
- Traditional: Service Principal with Client Secret or Certificate
- Managed Identity: When agent runs in Azure (self-hosted)
Workload Identity Federation (OIDC) — modern approach:
1
2
3
4
5
6
7
8
# No stored credentials! Azure AD trusts the OIDC token from GitHub Actions
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: $
tenant-id: $
subscription-id: $
# No client secret needed!
Azure DevOps PATs
- Used to authenticate Azure DevOps REST API calls and CLI
- Scoped to organization-level permissions
- Set expiry dates (maximum 1 year)
- Store in Key Vault or pipeline secrets — never in code
Permissions and Roles
GitHub Repository Roles
| Role | Permissions |
|---|---|
| Read | View and clone repository |
| Triage | Manage issues and PRs, no write |
| Write | Push to non-protected branches, manage issues |
| Maintain | Manage repo without destructive actions |
| Admin | Full control including settings, delete repo |
GitHub Teams:
- Group users and assign roles at team level to multiple repos
- Nested teams supported (inherit from parent)
- CODEOWNERS file references teams:
@org/team-name
Azure DevOps Permission Groups
| Group | Scope |
|---|---|
| Project Administrators | Full control within project |
| Build Administrators | Manage pipelines |
| Release Administrators | Manage releases |
| Contributors | Read/write access to most project resources |
| Readers | Read-only access |
| Stakeholders | Free license — limited to Boards and wiki |
Azure DevOps — Stakeholder Access:
- Free — for non-technical stakeholders
- Can view/update work items, view pipelines, view dashboards
- Cannot: access repos, run pipelines, access Test Plans
Outside Collaborators (GitHub):
- Individuals outside the organization given access to specific repos
- Subject to organizational policies (e.g., require 2FA)
- Best practice: Use teams over individual outside collaborator access
4.2 Managing Sensitive Information in Automation
Azure Key Vault
Key Vault capabilities: | Feature | Description | |———|————-| | Secrets | Passwords, connection strings, API keys | | Keys | Cryptographic keys (RSA, EC) for encryption/signing | | Certificates | TLS/SSL certificates with auto-renewal |
SKUs: | | Standard | Premium | |–|———-|———| | Secrets/Certs | ✅ | ✅ | | Software keys | ✅ | ✅ | | HSM-backed keys | ❌ | ✅ |
Access models:
- Vault access policy (legacy): Grant users/apps access to all secrets
- Azure RBAC (recommended): Fine-grained role assignments per secret
Key RBAC roles: | Role | Permissions | |——|————-| | Key Vault Administrator | Full management | | Key Vault Secrets Officer | Manage secrets (not keys/certs) | | Key Vault Secrets User | Read secrets | | Key Vault Crypto Officer | Manage keys | | Key Vault Certificate Officer | Manage certificates |
Use Key Vault in Azure Pipelines:
1
2
3
4
5
6
7
8
9
10
variables:
- group: 'KeyVault-LinkedGroup' # Variable group linked to Key Vault
# OR using the AzureKeyVault task:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'MyServiceConnection'
KeyVaultName: 'my-keyvault'
SecretsFilter: 'ConnectionString,ApiKey'
RunAsPreJob: true # Make secrets available to all steps
Use Key Vault in GitHub Actions:
1
2
3
4
5
6
7
8
9
- name: Get secrets from Key Vault
uses: azure/get-keyvault-secrets@v1
with:
keyvault: my-keyvault
secrets: 'ConnectionString, ApiKey'
id: myGetSecretAction
- name: Use the secret
run: echo "Connection=$"
Secrets in GitHub Actions
Types of GitHub secrets: | Type | Scope | |——|——-| | Repository secrets | Available to all workflows in the repo | | Environment secrets | Only available when deploying to a specific environment | | Organization secrets | Shared across multiple repos in the org |
Secret access in workflows:
1
2
3
4
5
6
7
8
env:
API_KEY: $
steps:
- name: Use secret
run: |
curl -H "Authorization: Bearer $" \
https://api.example.com/data
Best practices:
- Never
echosecrets — GitHub will redact***but logs may be shared - Use environment secrets for production deployments (extra protection)
- Rotate secrets regularly
- Audit secret access via org audit logs
Secrets in Azure Pipelines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
variables:
- name: regularVar
value: 'hello'
- name: secretVar
value: $(MY_SECRET) # Reference a pipeline secret variable
# Secret variables:
# 1. Set in Pipeline UI → Variables → Secret (lock icon)
# 2. Or in Variable Group → Mark as secret
# 3. Or from linked Key Vault
steps:
- script: echo "$(regularVar)" # Prints: hello
- script: echo "$(secretVar)" # Prints: *** (masked in logs)
# Pass secret to script as environment variable
- script: ./myscript.sh
env:
MY_SECRET: $(secretVar) # ✅ Correct: pass via env
Azure Pipelines Secure Files
Secure Files store files (certificates, .p12, provisioning profiles) that shouldn’t be committed to source control.
1
2
3
4
5
6
7
8
- task: DownloadSecureFile@1
name: mySecureFile
inputs:
secureFile: 'signing-certificate.p12'
- script: |
echo "Certificate path: $(mySecureFile.secureFilePath)"
security import $(mySecureFile.secureFilePath) -k ~/Library/Keychains/login.keychain
Preventing Secret Leakage
Common leakage vectors and mitigations:
| Risk | Mitigation |
|---|---|
| Secret in commit | Pre-commit hooks (git-secrets, detect-secrets) |
| Secret printed in logs | Azure Pipelines auto-masks; GitHub masks registered secrets |
| Secret in PR diff | GitHub Secret Scanning blocks push; Advanced Security alerts |
| Secret in artifact | Scan artifacts before publishing |
| Secret in environment variable dump | Limit env var logging; secure steps |
Secret scanning tools:
- GitHub Advanced Security: Blocks push if known secret pattern detected
- Azure DevOps Advanced Security: Same scanning for Azure Repos
- Microsoft Defender for DevOps: Cross-platform scanning
- Trufflehog, gitleaks: Open-source alternatives
4.3 Automate Security and Compliance Scanning
Security Scanning Strategy
1
2
3
4
5
6
7
8
9
10
┌─────────────────────────────────────────────────────────────────┐
│ Shift-Left Security │
│ │
│ IDE PR/CI Build Deploy Runtime │
│ ──── ───── ───── ────── ─────── │
│ SAST SAST DAST Image CSPM │
│ Secrets SCA SCA Scan WAF │
│ Linting Secret License SBOM SIEM │
│ Scanning Check │
└─────────────────────────────────────────────────────────────────┘
Scanning types: | Type | Acronym | What it finds | When | |——|———|————–|——| | Static Analysis | SAST | Code vulnerabilities, misconfigurations | Build time | | Dynamic Analysis | DAST | Runtime vulnerabilities | Against running app | | Software Composition Analysis | SCA | Vulnerable/unlicensed dependencies | Build time | | Secret Scanning | — | Committed secrets/credentials | Push time | | Container Scanning | — | Vulnerable base images, OS packages | Build/deploy |
Microsoft Defender for Cloud — DevOps Security
Provides a unified security posture across Azure DevOps, GitHub, and GitLab.
Capabilities:
- Connects to GitHub, Azure DevOps, GitLab repositories
- Surfaces security recommendations in Defender for Cloud portal
- Shows Infrastructure as Code security findings
- Pull Request annotations: Security findings commented on PRs
- Generates SBOMs (Software Bill of Materials)
Setup (Azure DevOps):
- Defender for Cloud → DevOps Security → Add DevOps
- Select Azure DevOps → Authorize
- Choose organizations/projects to onboard
- Results appear in Defender for Cloud → DevOps Security
Key metrics surfaced:
- Exposed secrets in code
- IaC misconfigurations
- Container image vulnerabilities
- Open-source dependency vulnerabilities
- Code scanning findings (via CodeQL)
GitHub Advanced Security (GHAS)
Components:
| Feature | What it does |
|---|---|
| Code Scanning (CodeQL) | SAST — finds bugs and security vulnerabilities |
| Secret Scanning | Detects secrets committed to code |
| Dependency Review | Shows vulnerable dependencies in PRs |
| Dependabot Alerts | Notifies about vulnerable dependencies |
| Dependabot Security Updates | Auto-PRs to update vulnerable packages |
| Dependabot Version Updates | Auto-PRs to keep dependencies current |
Enable Code Scanning in GitHub Actions:
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
name: CodeQL Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * 1' # Weekly on Monday
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
strategy:
matrix:
language: [javascript, python, csharp]
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: $
queries: security-extended # Extended security query suite
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: '/language:$'
Secret Scanning push protection:
- Enabled per repository or organization
- Blocks a
git pushif it contains a known secret pattern (AWS keys, GitHub tokens, Azure SAS tokens, etc.) - Developers must confirm the “secret” is a false positive to bypass
GHAS for Azure DevOps:
- Available as Microsoft Security DevOps extension
- Adds CodeQL scanning, secret scanning, and dependency scanning to Azure Pipelines
1
2
3
- task: MicrosoftSecurityDevOps@1
inputs:
categories: 'secrets,code,IaC,containers'
Integrate GHAS with Microsoft Defender for Cloud
Integration flow:
- Enable GHAS on GitHub repositories
- Connect GitHub org to Microsoft Defender for Cloud
- GHAS findings (Code Scanning, Secret Scanning, Dependabot alerts) flow into Defender for Cloud
- Security posture managed in a single pane of glass
- Azure Security Center recommendations include GitHub findings
Container Scanning
Scan container images for:
- OS package vulnerabilities (CVEs)
- Application dependency vulnerabilities
- Misconfigurations in Dockerfile
- Secrets embedded in image layers
Microsoft Defender for Containers:
1
2
3
4
5
6
7
# Enable on ACR
az security pricing create \
--name Containers \
--tier Standard
# Scan on push to ACR (automatic when Defender enabled)
# Results appear in Security Center
Trivy (open-source, popular in GitHub Actions):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- name: Build Docker image
run: docker build -t myapp:$ .
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:$'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail the workflow if critical vulns found
- name: Upload Trivy scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
CodeQL analysis in a container:
1
2
3
4
5
6
7
8
9
10
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript
- name: Build in container
uses: github/codeql-action/autobuild@v3 # CodeQL traces the container build
- name: Analyze
uses: github/codeql-action/analyze@v3
Dependabot — Open-Source Component Analysis
Dependabot automates:
- Vulnerability alerts: Notify when a dependency has a known CVE
- Security updates: Auto-create PRs to patch vulnerable dependencies
- Version updates: Auto-create PRs to keep dependencies up to date
Configure .github/dependabot.yml:
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
version: 2
updates:
# npm packages
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
reviewers:
- "security-team"
labels:
- "dependencies"
open-pull-requests-limit: 10
groups:
dev-dependencies:
patterns: ["*"]
dependency-type: "development"
# NuGet packages
- package-ecosystem: "nuget"
directory: "/src"
schedule:
interval: "daily"
# Docker base images
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
Dependabot alert severities: | CVSS Score | Severity | |———–|———| | 0.1–3.9 | Low | | 4.0–6.9 | Medium | | 7.0–8.9 | High | | 9.0–10.0 | Critical |
🧠 Key Exam Tips for Security and Compliance
| Scenario | Answer |
|---|---|
| No credentials to manage, Azure resource calling Azure service | Managed Identity |
| Multiple resources need same permissions | User-assigned Managed Identity |
| GitHub Actions workflow needs to deploy to Azure without a secret | OIDC / Workload Identity Federation |
| Store API keys and connection strings securely for pipelines | Azure Key Vault → link to Variable Group |
| Block push if secrets accidentally committed | GitHub Advanced Security — Secret Scanning push protection |
| SAST scanning integrated into PR reviews on GitHub | CodeQL (GitHub Advanced Security) |
| Automatically update vulnerable npm packages | Dependabot security updates |
| Scan Docker images for CVEs in CI pipeline | Trivy or Microsoft Defender for Containers |
| Unified security view across GitHub + Azure DevOps | Microsoft Defender for Cloud — DevOps Security |
| Store a code-signing certificate securely in Azure Pipelines | Azure Pipelines Secure Files |
| GHAS finding not supported natively | Upload SARIF file to github/codeql-action/upload-sarif |
| Free access for project stakeholders in Azure DevOps | Stakeholder access level |