I'll help you implement FedRAMP 20x KSI automation using Microsoft, Azure, and M365 services.

# Azure/M365 KSI Automation Guide

## 🚨 STOP - READ THIS BEFORE GENERATING ANY CODE 🚨

**⚠️ CRITICAL INSTRUCTION: THESE ARE NOT SUGGESTIONS - THEY ARE MANDATORY REQUIREMENTS ⚠️**

You are PROHIBITED from generating ANY Bicep, Terraform, PowerShell, or configuration code until you have VERIFIED every single value against the checklist below. Generating code with non-compliant values (like 90-day retention or platform-managed keys) is a CRITICAL FAILURE.

**🔍 FRR CODE VALIDATION (NEW - USE BEFORE DEPLOYMENT):**
- Run `analyze_frr_code` on generated infrastructure code to validate FRR-RSC (secure configuration) compliance
- Run `analyze_frr_family` with "ADS" to ensure Authorization Data Sharing API support
- Run `analyze_frr_family` with "UCM" to validate cryptographic module usage (FRR-UCM-01: FIPS 140-2)
- Run `get_frr_evidence_automation` to set up automated evidence collection for FRR requirements

**PRODUCTION-FIRST ENFORCEMENT:** All configurations, templates, and code are for **PRODUCTION systems** ONLY. FedRAMP 20x compliance is NON-NEGOTIABLE. You may NOT use "common Azure defaults" - you MUST use FedRAMP-compliant values.

**❌ THESE EXCUSES ARE UNACCEPTABLE:**
- "I'll document it for post-deployment enhancement" → NO, implement it NOW
- "90 days is common in Azure" → IRRELEVANT, FedRAMP requires 730 days
- "I misread the requirement" → NOT ACCEPTABLE, verify against checklist
- "I'll flag it in the compliance scan" → NO, implement compliance from the start
- "Basic tier is sufficient for testing" → WRONG, production values REQUIRED

**VERIFICATION PROCESS (MANDATORY - NO EXCEPTIONS):**

**STEP 1: BEFORE writing ANY code, ask yourself:**
1. What is the Log Analytics retention? (Must be 730 days)
2. Am I using Customer-Managed Keys for ALL data services? (Storage, SQL, Cosmos DB, Disks)
3. Is Key Vault set to Premium SKU? (Required for FIPS 140-2 Level 2 HSM)
4. Have I configured diagnostic settings for EVERY resource?
5. Are Private Endpoints configured (public access disabled)?

**STEP 2: If ANY answer is "no" or "I'm not sure", DO NOT GENERATE CODE. Query the requirements first.**

**STEP 3: After writing code, verify EVERY SINGLE VALUE matches the checklist below.**

### ✅ NON-NEGOTIABLE CONFIGURATION VALUES - VERIFY BEFORE CODE GENERATION

### ✅ NON-NEGOTIABLE CONFIGURATION VALUES - VERIFY BEFORE CODE GENERATION

**RULE: If you cannot verify 100% of these values in your generated code, STOP and query requirements instead of guessing.**

#### Encryption & Key Management (KSI-SVC-06) - ZERO TOLERANCE FOR PLATFORM-MANAGED KEYS
- [ ] **Customer-Managed Keys (CMK)**: MANDATORY for Storage, SQL, Cosmos DB, Disks, Event Hubs, Service Bus
  - ❌ NEVER generate `keySource: 'Microsoft.Storage'` (platform-managed keys)
  - ✅ ALWAYS use `keySource: 'Microsoft.Keyvault'` with `keyvaultproperties`
  - ❌ NEVER omit `keyVaultKeyUri` for Cosmos DB
  - ✅ ALWAYS configure `keyVaultKeyUri: 'https://{vault}.vault.azure.net/keys/{key}/{version}'`
- [ ] **Key Vault SKU**: MANDATORY `Premium` (FIPS 140-2 Level 2 HSM-backed keys)
  - ❌ NEVER use `name: 'standard'`
  - ✅ ALWAYS use `name: 'premium'`
- [ ] **Key Vault enabledForDiskEncryption**: MANDATORY `true` (Disk Encryption Sets requirement)
  - ❌ NEVER set to `false` or omit
  - ✅ ALWAYS include `enabledForDiskEncryption: true`
- [ ] **Separate encryption keys**: One key per service type (storage-key, sql-key, cosmos-key, disk-key)
- [ ] **Managed Identity**: SystemAssigned or UserAssigned with Key Vault access policies (unwrapKey, wrapKey, get)

#### Logging & Monitoring (KSI-MLA-01, KSI-MLA-02) - ZERO TOLERANCE FOR SHORT RETENTION
- [ ] **Log Analytics retention**: MANDATORY `730 days` (2 years)
  - ❌ NEVER use 90, 180, or 365 days
  - ✅ ALWAYS use `retentionInDays: 730` (Bicep) or `retention_in_days = 730` (Terraform)
  - **SOURCE**: NIST AU-11 Audit Record Retention, KSI-MLA-01 SIEM requirements, KSI-MLA-02 Audit Logging
- [ ] **Diagnostic settings**: MANDATORY for ALL Azure resources (Storage, SQL, VMs, Key Vault, Cosmos DB, etc.)
  - ❌ NEVER deploy resources without diagnostic settings
  - ✅ ALWAYS include `Microsoft.Insights/diagnosticSettings` resource linked to Log Analytics
- [ ] **Log categories**: Enable ALL log categories (Audit, Security, Admin, Alert, Access)
- [ ] **Metric retention**: MANDATORY align with log retention (730 days)
- [ ] **Workspace SKU**: MANDATORY `PerGB2018` or `Committed` (NEVER Free tier)

#### Network Security (KSI-CNA-01 through KSI-CNA-08) - ZERO TOLERANCE FOR PUBLIC ACCESS
- [ ] **Public access**: MANDATORY `Disabled` for Storage, SQL, Key Vault, Cosmos DB
  - ❌ NEVER use `publicNetworkAccess: 'Enabled'`
  - ✅ ALWAYS use `publicNetworkAccess: 'Disabled'` with Private Endpoints
- [ ] **TLS version**: MANDATORY `1.2` minimum (prefer `1.3` where supported)
  - ❌ NEVER use `minimumTlsVersion: 'TLS1_0'` or `'TLS1_1'`
  - ✅ ALWAYS use `minimumTlsVersion: 'TLS1_2'`
- [ ] **Private Endpoints**: MANDATORY for ALL PaaS services (Storage, SQL, Key Vault, Cosmos DB)
- [ ] **Network Security Groups**: MANDATORY with restrictive rules (deny-by-default)
- [ ] **Azure Firewall/Application Gateway**: MANDATORY for inbound traffic

#### Identity & Access (KSI-IAM-01 through KSI-IAM-07) - ZERO TOLERANCE FOR WEAK AUTHENTICATION
- [ ] **Managed Identity**: MANDATORY (prefer SystemAssigned, use UserAssigned for cross-resource)
  - ❌ NEVER use connection strings or API keys in code
  - ✅ ALWAYS use `identity: { type: 'SystemAssigned' }`
- [ ] **Disable Local Authentication**: MANDATORY for Cosmos DB, Storage, Service Bus, Event Hubs
  - ❌ NEVER set `disableLocalAuth: false` (allows shared key authentication)
  - ✅ ALWAYS use `disableLocalAuth: true` (enforce Azure AD authentication only)
  - **SOURCE**: KSI-IAM-01 (Azure AD enforcement), KSI-IAM-03 (disable legacy auth), NIST IA-2
- [ ] **RBAC assignments**: Use built-in roles (NEVER Owner/Contributor on production)
- [ ] **Conditional Access**: MANDATORY for MFA enforcement
- [ ] **Privileged Identity Management (PIM)**: MANDATORY for admin roles

#### High Availability & Resilience (KSI-AFR family) - ZERO TOLERANCE FOR SINGLE POINTS OF FAILURE
- [ ] **Availability Zones**: MANDATORY (ZoneRedundant or Zone1+Zone2+Zone3)
  - ❌ NEVER use `sku: { name: 'Standard_LRS' }` (no zone redundancy)
  - ✅ ALWAYS use `sku: { name: 'Standard_ZRS' }` or `'Standard_GZRS'`
- [ ] **Backup retention**: MANDATORY 30 days minimum (prefer 90+ days for production)
- [ ] **Geo-replication**: MANDATORY for critical data (GRS, GZRS, RA-GRS)
- [ ] **Auto-failover**: MANDATORY enabled for multi-region deployments

---

## 🛑 STOP - FINAL VERIFICATION BEFORE CODE GENERATION 🛑

**BEFORE you write a SINGLE LINE of Bicep/Terraform/PowerShell code, answer these questions:**

1. **Log Analytics retention**: Is it EXACTLY `730` days? (Not 90, not 365, EXACTLY 730)
2. **Customer-Managed Keys**: Have I configured CMK for EVERY data service? (Storage, SQL, Cosmos DB, Disks)
3. **Key Vault SKU**: Is it EXACTLY `premium`? (Not standard, EXACTLY premium)
4. **Cosmos DB keyVaultKeyUri**: Have I included this property with a valid Key Vault key reference?
5. **Public access**: Is it DISABLED for ALL PaaS services with Private Endpoints configured?
6. **Disable Local Auth**: Is `disableLocalAuth: true` set for Cosmos DB, Storage, Service Bus, Event Hubs?

**IF YOU CANNOT ANSWER "YES" TO ALL 6 QUESTIONS, DO NOT GENERATE CODE.**

**INSTEAD:** Query the requirements using available tools:
- `get_ksi_by_id` - Get specific KSI requirements
- `get_implementation_examples` - Get FedRAMP implementation examples
- `get_infrastructure_code_for_ksi` - Get compliant infrastructure templates

---

## 📝 MANDATORY COMMENT FORMAT - ALWAYS INCLUDE KSI IDs

**CRITICAL: Every compliance-related property MUST have a comment with the KSI ID and requirement.**

**Comment Format Template:**
```
// ✅ [KSI-XXX-##] Description (NIST control if applicable)
propertyName: value
```

**Required Comment Patterns:**

```bicep
// ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention for SIEM/audit logs (NIST AU-11)
retentionInDays: 730

// ✅ [KSI-SVC-06] Customer-Managed Keys for encryption key lifecycle control (NIST SC-12)
keySource: 'Microsoft.Keyvault'

// ✅ [KSI-SVC-06] Premium SKU required for FIPS 140-2 Level 2 HSM-backed keys (NIST SC-12)
sku: { name: 'premium' }

// ✅ [KSI-IAM-01, KSI-IAM-03] Disable local authentication - enforce Azure AD only (NIST IA-2)
disableLocalAuth: true

// ✅ [KSI-CNA-01] Disable public access - use Private Endpoints only (NIST SC-7)
publicNetworkAccess: 'Disabled'

// ✅ [KSI-SVC-06] Customer-Managed Key URI for Cosmos DB encryption (NIST SC-12, SC-28)
keyVaultKeyUri: cosmosEncryptionKey.properties.keyUriWithVersion

// ✅ [KSI-CNA-03] Private Endpoint for secure network connectivity (NIST SC-7)
resource storagePrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {

// ✅ [KSI-MLA-01] Diagnostic settings - send logs to Log Analytics with 730-day retention (NIST AU-2, AU-11)
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {

// ✅ [KSI-AFR-02] Zone-redundant storage for high availability (NIST CP-6, CP-9)
sku: { name: 'Standard_ZRS' }

// ✅ [KSI-CNA-01] Network Security Group with deny-by-default rules (NIST SC-7)
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {

// ✅ [KSI-IAM-07] Managed Identity for authentication without credentials (NIST IA-5)
identity: { type: 'SystemAssigned' }

// ✅ [KSI-CNA-01] TLS 1.2 minimum for secure communications (NIST SC-8, SC-13)
minimumTlsVersion: 'TLS1_2'
```

**Terraform Comment Format:**
```hcl
# ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention for SIEM/audit logs (NIST AU-11)
retention_in_days = 730

# ✅ [KSI-SVC-06] Customer-Managed Keys for encryption key lifecycle control (NIST SC-12)
customer_managed_key {
  key_vault_key_id = azurerm_key_vault_key.storage.id
}

# ✅ [KSI-IAM-01, KSI-IAM-03] Disable local authentication - enforce Azure AD only (NIST IA-2)
local_authentication_disabled = true

# ✅ [KSI-CNA-01] Disable public access - use Private Endpoints only (NIST SC-7)
public_network_access_enabled = false

# ✅ [KSI-CNA-03] Private Endpoint for secure network connectivity (NIST SC-7)
resource "azurerm_private_endpoint" "storage" {
```

**Why This Matters:**
1. **Traceability**: Auditors can map each setting to specific KSI requirements
2. **Maintainability**: Future developers understand WHY each setting exists
3. **Compliance Evidence**: Comments serve as inline documentation for FRR-ADS
4. **Code Reviews**: Reviewers can verify correct KSI alignment

**ENFORCEMENT: If you generate code without KSI ID comments, the validation tool will flag it.**

---

## ❌ EXPLICITLY FORBIDDEN PATTERNS - THESE WILL CAUSE COMPLIANCE FAILURES

**NEVER generate these patterns - they are CRITICAL FAILURES:**

```bicep
// ❌ CRITICAL FAILURE: 90-day retention (should be 730)
retentionInDays: 90

// ❌ CRITICAL FAILURE: Platform-managed keys (should be CMK)
keySource: 'Microsoft.Storage'

// ❌ CRITICAL FAILURE: Standard Key Vault (should be Premium)
sku: { name: 'standard' }

// ❌ CRITICAL FAILURE: Public access enabled (should be Disabled)
publicNetworkAccess: 'Enabled'

// ❌ CRITICAL FAILURE: Cosmos DB without CMK (should have keyVaultKeyUri)
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
  properties: {
    // Missing keyVaultKeyUri property
  }
}

// ❌ CRITICAL FAILURE: No diagnostic settings (should send logs to Log Analytics)
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  // Missing Microsoft.Insights/diagnosticSettings child resource
}
```

**✅ ALWAYS generate these patterns instead:**

```bicep
// ✅ CORRECT: 730-day retention (2 years)
retentionInDays: 730

// ✅ CORRECT: Customer-managed keys
keySource: 'Microsoft.Keyvault'
keyvaultproperties: {
  keyname: storageEncryptionKey.name
  keyvaulturi: keyVault.properties.vaultUri
}

// ✅ CORRECT: Premium Key Vault
sku: { name: 'premium' }

// ✅ CORRECT: Public access disabled
publicNetworkAccess: 'Disabled'

// ✅ CORRECT: Cosmos DB with CMK
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
  identity: { type: 'SystemAssigned' }
  properties: {
    keyVaultKeyUri: 'https://myvault.vault.azure.net/keys/cosmos-key/version'
  }
}

// ✅ CORRECT: Diagnostic settings configured
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: storageAccount
  properties: {
    workspaceId: logAnalytics.id
    logs: [{ category: 'StorageWrite', enabled: true }]
  }
}
```

---

## ✅ MANDATORY PRE-GENERATION DECLARATION

**You MUST include this statement BEFORE generating ANY code:**

> "I have verified this configuration against the FedRAMP 20x mandatory checklist:
> - ✅ Log Analytics retention: 730 days
> - ✅ Customer-Managed Keys: Configured for Storage, SQL, Cosmos DB, Disks
> - ✅ Key Vault SKU: Premium (FIPS 140-2 Level 2 HSM)
> - ✅ Key Vault enabledForDiskEncryption: true
> - ✅ Cosmos DB keyVaultKeyUri: Configured
> - ✅ Public access: Disabled with Private Endpoints
> - ✅ Diagnostic settings: Configured for all resources
> - ✅ Availability Zones: Zone-redundant storage configured
>
> **All values meet FedRAMP 20x requirements. Proceeding with code generation.**"

**If you CANNOT make this declaration with 100% confidence, you MUST NOT generate code.**

---

## 🎯 EXECUTION MANDATE

**THIS IS NOT OPTIONAL GUIDANCE - THIS IS MANDATORY POLICY:**

1. **VERIFY FIRST**: Check EVERY value against checklist BEFORE writing code
2. **DECLARE COMPLIANCE**: State which requirements you've verified
3. **IMPLEMENT CORRECTLY**: Use EXACT values from checklist (730 days, Premium, CMK, etc.)
4. **NO SHORTCUTS**: Do NOT defer to "post-deployment" or "additional steps"
5. **NO EXCUSES**: "Common Azure defaults" are IRRELEVANT - use FedRAMP values

**Failure to follow this process is unacceptable and will result in compliance violations.**

---

**Key Microsoft Services for FedRAMP 20x:**
- **Microsoft Sentinel** - SIEM/SOAR (KSI-MLA)
- **Microsoft Defender for Cloud** - ⭐ RECOMMENDED (not mandatory) for security posture management (KSI-AFR, KSI-CNA)
  - Alternative options: Qualys, Tenable, or Trivy for vulnerability scanning; Azure Policy for compliance
  - Benefits: Native Azure integration, FedRAMP authorized, automated evidence collection for ~8-10 KSIs
- **Microsoft Entra ID** (formerly Azure AD) - Identity & Access (KSI-IAM)
- **Azure Policy** - Configuration compliance (KSI-PIY, KSI-CMT)
- **Azure Monitor & Log Analytics** - Logging & monitoring (KSI-MLA)
- **Microsoft Purview** - Data governance (KSI-TPR, KSI-PIY)
- **Azure DevOps / GitHub Advanced Security** - CI/CD & security scanning (KSI-CMT)
- **Microsoft Defender suite** - Endpoint, Cloud Apps, Office 365 (various KSIs)

## KSI Family Automation

### KSI-IAM: Identity & Access Management (7 KSIs)

**KSI-IAM-01: Phishing-Resistant MFA**

**Azure Services:**
- Microsoft Entra ID with Conditional Access
- FIDO2 security keys or Windows Hello for Business
- Microsoft Authenticator (passwordless)

**Automation:**
```powershell
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "UserAuthenticationMethod.Read.All", "Policy.Read.All"

# Get MFA status for all users
$users = Get-MgUser -All
$mfaReport = @()

foreach ($user in $users) {
    $authMethods = Get-MgUserAuthenticationMethod -UserId $user.Id
    $hasFIDO2 = $authMethods | Where-Object { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.fido2AuthenticationMethod' }
    
    $mfaReport += [PSCustomObject]@{
        UserPrincipalName = $user.UserPrincipalName
        HasFIDO2 = ($hasFIDO2 -ne $null)
        MFAEnabled = $authMethods.Count -gt 1
    }
}

# Generate compliance report
$mfaReport | Export-Csv "mfa-compliance-$(Get-Date -Format yyyy-MM-dd).csv"
```

**Evidence Collection:**
- Microsoft Entra ID sign-in logs (Graph API)
- Authentication methods report
- Conditional Access policy export
- Store in Azure Blob Storage with immutability

**KSI-IAM-02 through IAM-07**

**Automation via Microsoft Graph API:**
```powershell
# IAM-02: Passwordless authentication
Get-MgBetaReportAuthenticationMethodUserRegistrationDetail | 
    Where-Object { $_.IsPasswordlessCapable -eq $true }

# IAM-05: Least privilege (Privileged Identity Management)
Get-MgRoleManagementDirectoryRoleAssignment | 
    Where-Object { $_.PrincipalType -eq "User" }

# IAM-06: Suspicious activity detection
# Configure Microsoft Entra ID Protection
$riskDetections = Get-MgRiskDetection -Top 100
$riskDetections | Export-Csv "risk-detections-$(Get-Date -Format yyyy-MM-dd).csv"
```

**Automated Evidence:**
- Use Azure Logic Apps to collect daily reports
- Store in Azure Storage with compliance tags
- Integrate with Sentinel for alerting

### KSI-MLA: Monitoring, Logging & Analysis (5 KSIs)

**KSI-MLA-01: Centralized Logging (SIEM)**

**Azure Services:**
- Microsoft Sentinel (Azure-native SIEM)
- Log Analytics Workspace
- Azure Monitor Agent

**Automation:**
```bash
# Deploy Sentinel workspace with Azure CLI
az sentinel workspace create \
    --resource-group rg-fedramp \
    --name sentinel-fedramp \
    --location eastus

# Enable all data connectors
az sentinel data-connector create \
    --resource-group rg-fedramp \
    --workspace-name sentinel-fedramp \
    --kind AzureActiveDirectory

# Configure log retention (730 days / 2 years for FedRAMP compliance)
az monitor log-analytics workspace update \
    --resource-group rg-fedramp \
    --workspace-name sentinel-fedramp \
    --retention-time 730
```

**Evidence Collection via KQL:**
```kusto
// Daily log ingestion report
Usage
| where TimeGenerated > ago(1d)
| where IsBillable == true
| summarize TotalGB = sum(Quantity) / 1000 by DataType
| order by TotalGB desc

// Store results in Azure Data Explorer for historical tracking
```

**KSI-MLA-02: Audit Logging**

**Automation:**
```powershell
# Enable diagnostic settings for all Azure resources
$resources = Get-AzResource

foreach ($resource in $resources) {
    $diagnosticSettings = @{
        Name = "fedramp-logging"
        ResourceId = $resource.ResourceId
        WorkspaceId = "/subscriptions/.../workspaces/sentinel-fedramp"
        Enabled = $true
        Category = @("AuditEvent", "Administrative", "Security")
    }
    
    Set-AzDiagnosticSetting @diagnosticSettings
}
```

**KSI-MLA-05: Infrastructure as Code**

**Azure Services:**
- Azure Repos (for Bicep/ARM/Terraform)
- Azure DevOps Pipelines
- Azure Policy for IaC validation

**Automation:**
```yaml
# Azure Pipeline to validate IaC compliance
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: AzureCLI@2
  inputs:
    azureSubscription: 'fedramp-connection'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      # Scan Bicep files
      az bicep build --file main.bicep
      
      # Run Azure Policy compliance check
      az policy state list --resource-group rg-fedramp \
        --query "[?complianceState=='NonCompliant']" > compliance-report.json
      
      # Upload to evidence storage
      az storage blob upload \
        --account-name fedrampevidence \
        --container-name iac-compliance \
        --name "compliance-$(date +%Y-%m-%d).json" \
        --file compliance-report.json
```

### KSI-AFR: Automated Findings & Remediation (5 KSIs)

**KSI-AFR-01: Vulnerability Scanning**

**Azure Services:**
- Microsoft Defender for Cloud
- Microsoft Defender for Containers
- GitHub Advanced Security (for code)

**Automation:**
```powershell
# Get vulnerability assessment findings from Defender for Cloud
$vulnerabilities = Get-AzSecurityTask | Where-Object { 
    $_.SecurityTaskParameters.Name -match "Vulnerability" 
}

# Generate FedRAMP-compliant report
$vulnReport = $vulnerabilities | Select-Object @{
    Name = 'FindingId'; Expression = { $_.Name }
}, @{
    Name = 'Severity'; Expression = { $_.SecurityTaskParameters.Severity }
}, @{
    Name = 'Resource'; Expression = { $_.ResourceId }
}, @{
    Name = 'DetectedDate'; Expression = { $_.TimeGenerated }
}, @{
    Name = 'DueDate'; Expression = { 
        # Calculate based on FRR-VDR timeframes
        $days = switch ($_.SecurityTaskParameters.Severity) {
            'Critical' { 7 }
            'High' { 15 }
            'Medium' { 30 }
            default { 90 }
        }
        (Get-Date).AddDays($days)
    }
}

$vulnReport | Export-Csv "vuln-report-$(Get-Date -Format yyyy-MM-dd).csv"
```

**Automated Remediation:**
```powershell
# Enable automatic remediation in Defender for Cloud
Update-AzSecurityAutoProvisioningSetting -Name "default" -EnableAutoProvision

# Create Azure Logic App for ticket creation
# When new vulnerability detected -> Create Azure DevOps work item
```

**KSI-AFR-04: Continuous Scanning**

**Automation:**
```bash
# Enable Defender for Cloud on all subscriptions
az security pricing create \
    --name VirtualMachines \
    --tier Standard

az security pricing create \
    --name Containers \
    --tier Standard

# Configure continuous export to Log Analytics
az security automation create \
    --resource-group rg-fedramp \
    --name export-vulnerabilities \
    --location eastus \
    --scopes "/subscriptions/{subscription-id}" \
    --sources "Assessments" \
    --actions '[{
        "actionType": "LogAnalytics",
        "workspaceResourceId": "/subscriptions/.../workspaces/sentinel-fedramp"
    }]'
```

### KSI-CMT: Change Management & Testing (4 KSIs)

**KSI-CMT-01: Track Changes**

**Azure Services:**
- Azure DevOps (change tracking)
- Azure Repos (version control)
- Azure Resource Graph (infrastructure changes)

**Automation:**
```kusto
// Query all Azure resource changes in last 30 days
resourcechanges
| where timestamp > ago(30d)
| extend changeType = properties.changeType
| extend changedBy = properties.changeAttributes.changedBy
| project timestamp, changeType, changedBy, resourceId = id, changes = properties.changes
| order by timestamp desc

// Export to CSV for evidence
```

**Change Notification Automation:**
```powershell
# Monitor Azure Activity Log for significant changes
$activityLogs = Get-AzActivityLog -StartTime (Get-Date).AddDays(-1)

$significantChanges = $activityLogs | Where-Object {
    $_.OperationName.Value -in @(
        'Microsoft.Compute/virtualMachines/write',
        'Microsoft.Network/networkSecurityGroups/write',
        'Microsoft.KeyVault/vaults/write'
    )
}

# Send to Teams channel via webhook
foreach ($change in $significantChanges) {
    $body = @{
        text = "Significant Change Detected: $($change.OperationName.Value) by $($change.Caller)"
    } | ConvertTo-Json
    
    Invoke-RestMethod -Uri $env:TEAMS_WEBHOOK_URL -Method Post -Body $body -ContentType 'application/json'
}
```

**KSI-CMT-03: Automated Testing**

**Azure DevOps Pipeline:**
```yaml
# Complete FedRAMP testing pipeline
stages:
- stage: SecurityScanning
  jobs:
  - job: SAST
    steps:
    - task: GitHubAdvancedSecurity@1
      displayName: 'Run SAST'
  
  - job: ContainerScan
    steps:
    - task: AzureContainerRegistry@2
      displayName: 'Scan container images'
  
  - job: IaCValidation
    steps:
    - task: AzureCLI@2
      displayName: 'Validate Bicep/Terraform'
      inputs:
        scriptType: 'bash'
        inlineScript: |
          az bicep build --file main.bicep
          terraform validate

- stage: Deploy
  dependsOn: SecurityScanning
  condition: succeeded()
  jobs:
  - deployment: DeployToStaging
    environment: staging
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureResourceManagerTemplateDeployment@3

- stage: IntegrationTests
  jobs:
  - job: RunTests
    steps:
    - task: AzureCLI@2
      displayName: 'Run integration tests'

# Store test results in Azure Blob
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: 'test-results'
    artifact: 'fedramp-test-evidence'
```

### KSI-CNA: Cloud-Native Architecture (8 KSIs)

**KSI-CNA-01: Restrict Network Traffic**

**Azure Services:**
- Network Security Groups (NSGs)
- Azure Firewall
- Azure Policy

**Automation:**
```powershell
# Audit NSG rules for compliance
$nsgs = Get-AzNetworkSecurityGroup

$nonCompliantRules = @()
foreach ($nsg in $nsgs) {
    foreach ($rule in $nsg.SecurityRules) {
        if ($rule.SourceAddressPrefix -eq "*" -and $rule.Direction -eq "Inbound") {
            $nonCompliantRules += [PSCustomObject]@{
                NSG = $nsg.Name
                Rule = $rule.Name
                Issue = "Allows traffic from any source"
                Severity = "High"
            }
        }
    }
}

$nonCompliantRules | Export-Csv "nsg-audit-$(Get-Date -Format yyyy-MM-dd).csv"
```

**Azure Policy for Network Compliance:**
```json
{
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
        },
        {
          "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
          "equals": "*"
        },
        {
          "field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
          "equals": "Allow"
        }
      ]
    },
    "then": {
      "effect": "deny"
    }
  }
}
```

**KSI-CNA-04: Immutable Infrastructure**

**Azure Services:**
- Azure VM Image Builder
- Azure Container Registry with image immutability
- Azure Kubernetes Service (AKS) with node pools

**Automation:**
```bash
# Enable ACR image immutability
az acr config retention update \
    --registry fedrampregistry \
    --status enabled \
    --days 365 \
    --type UntaggedManifests

# Configure AKS for immutable nodes
az aks nodepool update \
    --resource-group rg-fedramp \
    --cluster-name aks-fedramp \
    --name nodepool1 \
    --mode System \
    --enable-node-public-ip false

# Evidence: Daily snapshot of infrastructure state
az resource list --query "[].{Name:name, Type:type, Location:location}" \
    -o json > "infrastructure-state-$(date +%Y-%m-%d).json"
```

### KSI-INR: Incident Notification & Response (3 KSIs)

**Azure Services:**
- Microsoft Sentinel (SIEM/SOAR)
- Azure Logic Apps (automation)
- Microsoft Teams (notifications)

**Automation:**
```powershell
# Create Sentinel Analytics Rule for incident detection
$rule = @{
    DisplayName = "Suspicious Sign-in Activity"
    Query = @"
SigninLogs
| where ResultType != 0
| where TimeGenerated > ago(1h)
| summarize FailedAttempts = count() by UserPrincipalName, IPAddress
| where FailedAttempts > 5
"@
    Severity = "High"
    Enabled = $true
}

New-AzSentinelAlertRule @rule -WorkspaceName "sentinel-fedramp"
```

**Automated Incident Response Playbook:**
```json
{
  "type": "Microsoft.Logic/workflows",
  "properties": {
    "definition": {
      "triggers": {
        "When_Sentinel_Incident_Created": {
          "type": "ApiConnection",
          "inputs": {
            "host": {
              "connection": {
                "name": "@parameters('$connections')['azuresentinel']"
              }
            }
          }
        }
      },
      "actions": {
        "Post_to_Teams": {
          "type": "ApiConnection",
          "inputs": {
            "host": {
              "connection": {
                "name": "@parameters('$connections')['teams']"
              }
            },
            "method": "post",
            "body": {
              "message": "New Security Incident: @{triggerBody()?['title']}"
            }
          }
        },
        "Create_ServiceNow_Ticket": {
          "type": "ApiConnection",
          "runAfter": {
            "Post_to_Teams": ["Succeeded"]
          }
        },
        "Store_Incident_Evidence": {
          "type": "ApiConnection",
          "inputs": {
            "host": {
              "connection": {
                "name": "@parameters('$connections')['azureblob']"
              }
            },
            "method": "put",
            "path": "/evidence/incident-@{triggerBody()?['incidentNumber']}.json",
            "body": "@triggerBody()"
          }
        }
      }
    }
  }
}
```

### KSI-RPL: Recovery Planning (3 KSIs)

**KSI-RPL-03: Backup Testing**

**Azure Services:**
- Azure Backup
- Azure Site Recovery
- Azure Automation

**Automation:**
```powershell
# Automated backup verification
$vaults = Get-AzRecoveryServicesVault

$backupReport = @()
foreach ($vault in $vaults) {
    Set-AzRecoveryServicesVaultContext -Vault $vault
    
    $containers = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVM
    
    foreach ($container in $containers) {
        $items = Get-AzRecoveryServicesBackupItem -Container $container -WorkloadType AzureVM
        
        foreach ($item in $items) {
            $rp = Get-AzRecoveryServicesBackupRecoveryPoint -Item $item | Select-Object -First 1
            
            $backupReport += [PSCustomObject]@{
                VM = $item.Name
                LastBackup = $rp.RecoveryPointTime
                Status = $item.ProtectionStatus
                DaysSinceBackup = ((Get-Date) - $rp.RecoveryPointTime).Days
                Compliant = ((Get-Date) - $rp.RecoveryPointTime).Days -le 1
            }
        }
    }
}

$backupReport | Export-Csv "backup-compliance-$(Get-Date -Format yyyy-MM-dd).csv"

# Alert if backups are stale
$staleBackups = $backupReport | Where-Object { -not $_.Compliant }
if ($staleBackups) {
    # Send alert via Teams/Email
}
```

**Automated DR Testing:**
```bash
# Schedule quarterly DR test with Azure Automation
az automation runbook create \
    --resource-group rg-fedramp \
    --automation-account-name automation-fedramp \
    --name "QuarterlyDRTest" \
    --type PowerShell \
    --location eastus

# Create schedule for quarterly execution
az automation schedule create \
    --resource-group rg-fedramp \
    --automation-account-name automation-fedramp \
    --name "QuarterlyDRSchedule" \
    --frequency Quarter \
    --interval 1
```

### KSI-PIY: Platform Investment (10 KSIs)

**KSI-PIY-01: Automated Inventory**

**Azure Services:**
- Azure Resource Graph
- Microsoft Defender for Cloud
- Azure Policy

**Automation:**
```kusto
// Complete Azure resource inventory
Resources
| project 
    ResourceId = id,
    Name = name,
    Type = type,
    Location = location,
    ResourceGroup = resourceGroup,
    SubscriptionId = subscriptionId,
    Tags = tags,
    CreatedDate = properties.createdTime,
    ModifiedDate = properties.changedTime
| join kind=leftouter (
    SecurityResources
    | where type == "microsoft.security/assessments"
    | project ResourceId = id, SecurityScore = properties.status.code
) on ResourceId
| order by ModifiedDate desc

// Export daily to Blob Storage
```

**Automated Configuration Baseline:**
```powershell
# Use Azure Policy Guest Configuration
$guestConfig = @{
    Name = "FedRAMP-Baseline-Windows"
    PolicyDefinitionId = "/providers/Microsoft.Authorization/policyDefinitions/..."
    Scope = "/subscriptions/{subscription-id}"
}

New-AzPolicyAssignment @guestConfig

# Daily compliance report
Get-AzPolicyState -PolicyAssignmentName "FedRAMP-Baseline-Windows" |
    Select-Object ResourceId, ComplianceState, PolicyDefinitionAction |
    Export-Csv "config-compliance-$(Get-Date -Format yyyy-MM-dd).csv"
```

### KSI-SVC: Service Management & Delivery (10 KSIs)

**KSI-SVC-06: Secret Management**

**Azure Services:**
- Azure Key Vault (Premium SKU for HSM-backed keys)
- Managed Identity
- Azure Monitor

**CRITICAL: Customer-Managed Keys (CMK) Requirement**

FedRAMP 20x SC-12 (Cryptographic Key Establishment and Management) requires organizations
to control the full key lifecycle including revocation capability. This necessitates 
customer-managed keys (CMK) for Moderate/High impact levels:

**Why CMK is Required:**
- **SC-12 Compliance**: Requires control over cryptographic key establishment and management
- **Key Revocation**: Platform-managed keys (PMK) are controlled by Microsoft - limited revocation capability
- **CMK Benefits**: Full lifecycle control (rotation, revocation, versioning, BYOK)
- **FIPS 140-2 Level 2**: Key Vault Premium provides HSM-backed keys (required for CMK)

**Resources Requiring CMK:**
1. **Storage Accounts**: Use `keySource: 'Microsoft.Keyvault'` (not `'Microsoft.Storage'`)
2. **SQL Databases**: Configure TDE with customer-managed key
3. **Managed Disks**: Use Disk Encryption Sets with Key Vault key
4. **Cosmos DB**: Configure `keyVaultKeyUri` as **TOP-LEVEL property** (NOT inside encryption object)
5. **Azure Data Lake**: Use customer-managed key encryption

**⚠️ CRITICAL: Cosmos DB CMK Common Mistakes:**
- ❌ WRONG: `properties: { encryption: { keyVaultKeyUri: '...' } }` - NO encryption object exists
- ✅ CORRECT: `properties: { keyVaultKeyUri: '...' }` - keyVaultKeyUri is a direct property
- ❌ WRONG: `properties: { diagnosticLogSettings: {...} }` - This property doesn't exist
- ✅ CORRECT: Separate `Microsoft.Insights/diagnosticSettings` resource with `scope: cosmosDb`

**CMK Configuration Example (Bicep):**
```bicep
// Step 1: Key Vault with Premium SKU (FIPS 140-2 Level 2 HSM)
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: 'kv-fedramp-cmk'
  location: location
  properties: {
    sku: {
      family: 'A'
      name: 'premium'  // Required for CMK with HSM
    }
    tenantId: subscription().tenantId
    enableSoftDelete: true
    softDeleteRetentionInDays: 90
    enablePurgeProtection: true
    enabledForDiskEncryption: true  // Required for Disk Encryption Sets
    enableRbacAuthorization: true
  }
}

// Step 2: Encryption keys
resource storageEncryptionKey 'Microsoft.KeyVault/vaults/keys@2023-07-01' = {
  parent: keyVault
  name: 'storage-encryption-key'
  properties: {
    kty: 'RSA'
    keySize: 2048
    keyOps: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']
  }
}

resource cosmosEncryptionKey 'Microsoft.KeyVault/vaults/keys@2023-07-01' = {
  parent: keyVault
  name: 'cosmos-encryption-key'
  properties: {
    kty: 'RSA'
    keySize: 2048
    keyOps: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']
  }
}

// Step 3: Storage with CMK
resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  identity: {
    type: 'SystemAssigned'  // ✅ [KSI-IAM-07] Managed Identity for Key Vault access (NIST IA-5)
  }
  properties: {
    encryption: {
      keySource: 'Microsoft.Keyvault'  // ✅ [KSI-SVC-06] Customer-Managed Keys (NIST SC-12)
      keyvaultproperties: {
        keyname: storageEncryptionKey.name
        keyvaulturi: keyVault.properties.vaultUri
      }
    }
  }
}

// Step 4: Cosmos DB with CMK
resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
  name: cosmosAccountName
  location: location
  kind: 'GlobalDocumentDB'
  identity: {
    type: 'SystemAssigned'  // ✅ [KSI-IAM-07] Managed Identity for Key Vault access (NIST IA-5)
  }
  properties: {
    databaseAccountOfferType: 'Standard'
    consistencyPolicy: {
      defaultConsistencyLevel: 'Session'
    }
    locations: [{
      locationName: location
      failoverPriority: 0
      isZoneRedundant: false
    }]
    keyVaultKeyUri: cosmosEncryptionKey.properties.keyUriWithVersion  // ✅ [KSI-SVC-06] Customer-Managed Key (NIST SC-12, SC-28)
    disableLocalAuth: true  // ✅ [KSI-IAM-01, KSI-IAM-03] Force Azure AD authentication (NIST IA-2)
    publicNetworkAccess: 'Disabled'  // ✅ [KSI-CNA-01] Disable public access (NIST SC-7)
    networkAcls: {
      defaultAction: 'Deny'  // ✅ [KSI-CNA-01] Deny-by-default network rules (NIST SC-7)
    }
  }
}

// Step 4b: Diagnostic settings for Cosmos DB (separate resource, NOT a property)
resource cosmosDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'cosmos-diagnostics'
  scope: cosmosDb
  properties: {
    workspaceId: logAnalytics.id  // ✅ [KSI-MLA-01] Send logs to Log Analytics (NIST AU-2)
    logs: [
      {
        category: 'DataPlaneRequests'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 730  // ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention (NIST AU-11)
        }
      }
      {
        category: 'QueryRuntimeStatistics'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 730  // ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention (NIST AU-11)
        }
      }
      {
        category: 'PartitionKeyStatistics'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 730  // ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention (NIST AU-11)
        }
      }
    ]
    metrics: [
      {
        category: 'Requests'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 730  // ✅ [KSI-MLA-01, KSI-MLA-02] 730-day retention (NIST AU-11)
        }
      }
    ]
  }
}

// Step 5: Grant Key Vault access to storage
resource storageKeyVaultAccess 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = {
  parent: keyVault
  name: 'add'
  properties: {
    accessPolicies: [{
      tenantId: storage.identity.tenantId
      objectId: storage.identity.principalId
      permissions: {
        keys: ['get', 'unwrapKey', 'wrapKey']
      }
    }]
  }
}

// Step 6: Grant Key Vault access to Cosmos DB
resource cosmosKeyVaultAccess 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = {
  parent: keyVault
  name: 'add'
  properties: {
    accessPolicies: [{
      tenantId: cosmosDb.identity.tenantId
      objectId: cosmosDb.identity.principalId
      permissions: {
        keys: ['get', 'unwrapKey', 'wrapKey']
      }
    }]
  }
  dependsOn: [
    storageKeyVaultAccess  // Chain access policies to avoid conflicts
  ]
}
```

**CMK Configuration Example (Terraform):**
```hcl
# Step 1: Key Vault with Premium SKU
resource "azurerm_key_vault" "cmk" {
  name                       = "kv-fedramp-cmk"
  location                   = var.location
  resource_group_name        = azurerm_resource_group.rg.name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  sku_name                   = "premium"  # Required for CMK with HSM
  soft_delete_retention_days = 90
  purge_protection_enabled   = true
  enabled_for_disk_encryption = true  # Required for Disk Encryption Sets
  enable_rbac_authorization  = true
}

# Step 2: Encryption keys
resource "azurerm_key_vault_key" "storage" {
  name         = "storage-encryption-key"
  key_vault_id = azurerm_key_vault.cmk.id
  key_type     = "RSA"
  key_size     = 2048
  key_opts     = ["decrypt", "encrypt", "unwrapKey", "wrapKey"]
}

resource "azurerm_key_vault_key" "cosmos" {
  name         = "cosmos-encryption-key"
  key_vault_id = azurerm_key_vault.cmk.id
  key_type     = "RSA"
  key_size     = 2048
  key_opts     = ["decrypt", "encrypt", "unwrapKey", "wrapKey"]
}

# Step 3: Storage with CMK
resource "azurerm_storage_account" "evidence" {
  name                     = "stfedrampevidence"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
  
  identity {
    type = "SystemAssigned"
  }
  
  customer_managed_key {
    key_vault_key_id = azurerm_key_vault_key.storage.id
  }
}

# Step 4: Cosmos DB with CMK
resource "azurerm_cosmosdb_account" "db" {
  name                = "cosmos-fedramp-db"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name
  offer_type          = "Standard"
  kind                = "GlobalDocumentDB"
  
  consistency_policy {
    consistency_level = "Session"
  }
  
  geo_location {
    location          = var.location
    failover_priority = 0
  }
  
  identity {
    type = "SystemAssigned"
  }
  
  # CMK requirement
  key_vault_key_id = azurerm_key_vault_key.cosmos.id
}

# Step 5: Grant Key Vault access to storage
resource "azurerm_key_vault_access_policy" "storage" {
  key_vault_id = azurerm_key_vault.cmk.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_storage_account.evidence.identity[0].principal_id
  
  key_permissions = ["Get", "UnwrapKey", "WrapKey"]
}

# Step 6: Grant Key Vault access to Cosmos DB
resource "azurerm_key_vault_access_policy" "cosmos" {
  key_vault_id = azurerm_key_vault.cmk.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_cosmosdb_account.db.identity[0].principal_id
  
  key_permissions = ["Get", "UnwrapKey", "WrapKey"]
  
  depends_on = [
    azurerm_key_vault_access_policy.storage  # Chain to avoid conflicts
  ]
}
```

**Automation:**
```powershell
# Audit Key Vault access
$vaults = Get-AzKeyVault

$accessReport = @()
foreach ($vault in $vaults) {
    # Get diagnostic logs
    $logs = Get-AzDiagnosticSetting -ResourceId $vault.ResourceId
    
    # Query access logs
    $query = @"
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where TimeGenerated > ago(30d)
| summarize AccessCount = count() by CallerIPAddress, identity_claim_upn_s, OperationName
"@
    
    $results = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query
    
    $accessReport += $results.Results
}

$accessReport | Export-Csv "keyvault-access-$(Get-Date -Format yyyy-MM-dd).csv"
```

**Secret Rotation Automation:**
```powershell
# Azure Function for automatic secret rotation
param($Timer)

$secrets = Get-AzKeyVaultSecret -VaultName "fedramp-vault"

foreach ($secret in $secrets) {
    $daysUntilExpiry = ($secret.Expires - (Get-Date)).Days
    
    if ($daysUntilExpiry -lt 30) {
        # Trigger rotation workflow
        # Example: Rotate database connection string
        if ($secret.Name -like "*-db-*") {
            # Generate new password
            $newPassword = -join ((65..90) + (97..122) + (48..57) + (33,35,36,37,38,42) | Get-Random -Count 20 | % {[char]$_})
            
            # Update database
            # Update Key Vault
            Set-AzKeyVaultSecret -VaultName "fedramp-vault" -Name $secret.Name -SecretValue (ConvertTo-SecureString $newPassword -AsPlainText -Force)
            
            # Log rotation event
            Write-Host "Rotated secret: $($secret.Name)"
        }
    }
}
```

---

## 🔒 CRITICAL: Private Endpoints Configuration (KSI-CNA-01, KSI-CNA-03)

**⚠️ WARNING: Setting `publicNetworkAccess: 'Disabled'` without Private Endpoints will make resources COMPLETELY INACCESSIBLE!**

**You MUST configure Private Endpoints when disabling public access. This is NOT optional.**

### Why Private Endpoints Are MANDATORY:

1. **Functional Requirement**: Resources are unreachable without Private Endpoints
2. **KSI-CNA-01**: Network Segmentation (private connectivity for PaaS)
3. **KSI-CNA-03**: Private Network Connectivity
4. **NIST SC-7**: Boundary Protection

### Complete Private Endpoint Architecture:

```bicep
// ============================================================================
// STEP 1: Virtual Network with NSG-protected subnets
// ============================================================================
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {
  name: 'nsg-fedramp-private-endpoints'
  location: location
  properties: {
    securityRules: [
      {
        name: 'DenyAllInbound'
        properties: {
          priority: 4096
          direction: 'Inbound'
          access: 'Deny'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
  name: 'vnet-fedramp-private'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: ['10.0.0.0/16']
    }
    subnets: [
      {
        name: 'snet-private-endpoints'
        properties: {
          addressPrefix: '10.0.1.0/24'
          networkSecurityGroup: { id: nsg.id }
          privateEndpointNetworkPolicies: 'Disabled'  // ✅ [KSI-CNA-03] Required for Private Endpoints (NIST SC-7)
        }
      }
      {
        name: 'snet-application'
        properties: {
          addressPrefix: '10.0.2.0/24'
          networkSecurityGroup: { id: nsg.id }  // ✅ [KSI-CNA-01] NSG for network segmentation (NIST SC-7)
        }
      }
    ]
  }
}

// ============================================================================
// STEP 2: Private DNS Zones (required for name resolution)
// ============================================================================
resource privateDnsZoneStorage 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.blob.core.windows.net'  // ✅ [KSI-CNA-03] Private DNS for Storage (NIST SC-7)
  location: 'global'
}

resource privateDnsZoneCosmosDb 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.documents.azure.com'  // ✅ [KSI-CNA-03] Private DNS for Cosmos DB (NIST SC-7)
  location: 'global'
}

resource privateDnsZoneKeyVault 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.vaultcore.azure.net'  // ✅ [KSI-CNA-03] Private DNS for Key Vault (NIST SC-7)
  location: 'global'
}

// Link DNS zones to VNet
resource privateDnsZoneLinkStorage 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZoneStorage
  name: '${vnet.name}-link'
  location: 'global'
  properties: {
    registrationEnabled: false  // ✅ [KSI-CNA-03] Manual DNS registration (NIST SC-7)
    virtualNetwork: { id: vnet.id }
  }
}

resource privateDnsZoneLinkCosmosDb 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZoneCosmosDb
  name: '${vnet.name}-link'
  location: 'global'
  properties: {
    registrationEnabled: false  // ✅ [KSI-CNA-03] Manual DNS registration (NIST SC-7)
    virtualNetwork: { id: vnet.id }
  }
}

resource privateDnsZoneLinkKeyVault 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZoneKeyVault
  name: '${vnet.name}-link'
  location: 'global'
  properties: {
    registrationEnabled: false  // ✅ [KSI-CNA-03] Manual DNS registration (NIST SC-7)
    virtualNetwork: { id: vnet.id }
  }
}

// ============================================================================
// STEP 3: Storage Account with Private Endpoint
// ============================================================================
resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'stfedrampevidence'
  location: location
  sku: { name: 'Standard_GRS' }  // ✅ [KSI-AFR-09] Geo-redundant storage for durability (NIST CP-6, CP-9)
  kind: 'StorageV2'
  identity: { type: 'SystemAssigned' }  // ✅ [KSI-IAM-07] Managed Identity for Key Vault access (NIST IA-5)
  properties: {
    publicNetworkAccess: 'Disabled'  // ✅ [KSI-CNA-01] Disable public access (NIST SC-7)
    minimumTlsVersion: 'TLS1_2'  // ✅ [KSI-CNA-08] TLS 1.2 minimum (NIST SC-8)
    encryption: {
      keySource: 'Microsoft.Keyvault'  // ✅ [KSI-SVC-06] Customer-Managed Keys (NIST SC-12, SC-28)
      keyvaultproperties: {
        keyname: storageEncryptionKey.name
        keyvaulturi: keyVault.properties.vaultUri
      }
    }
  }
}

resource storagePrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {
  name: 'pe-storage-blob'
  location: location
  properties: {
    subnet: {
      id: '${vnet.id}/subnets/snet-private-endpoints'  // ✅ [KSI-CNA-03] Private Endpoint in dedicated subnet (NIST SC-7)
    }
    privateLinkServiceConnections: [{
      name: 'storage-blob-connection'
      properties: {
        privateLinkServiceId: storage.id  // ✅ [KSI-CNA-03] Link to Storage Account (NIST SC-7)
        groupIds: ['blob']  // ✅ [KSI-CNA-03] Target blob service (subresource: blob, file, table, queue)
      }
    }]
  }
}

resource storagePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = {
  parent: storagePrivateEndpoint
  name: 'default'
  properties: {
    privateDnsZoneConfigs: [{
      name: 'storage-blob-config'
      properties: {
        privateDnsZoneId: privateDnsZoneStorage.id  // ✅ [KSI-CNA-03] Link to Private DNS Zone (NIST SC-7)
      }
    }]
  }
}

// ============================================================================
// STEP 4: Cosmos DB with Private Endpoint
// ============================================================================
resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
  name: 'cosmos-fedramp-db'
  location: location
  kind: 'GlobalDocumentDB'
  identity: { type: 'SystemAssigned' }  // ✅ [KSI-IAM-07] Managed Identity for Key Vault access (NIST IA-5)
  properties: {
    databaseAccountOfferType: 'Standard'
    consistencyPolicy: { defaultConsistencyLevel: 'Session' }
    locations: [{
      locationName: location
      failoverPriority: 0
      isZoneRedundant: false
    }]
    keyVaultKeyUri: cosmosEncryptionKey.properties.keyUriWithVersion  // ✅ [KSI-SVC-06] Customer-Managed Key (NIST SC-12, SC-28)
    disableLocalAuth: true  // ✅ [KSI-IAM-01, KSI-IAM-03] Force Azure AD authentication (NIST IA-2)
    publicNetworkAccess: 'Disabled'  // ✅ [KSI-CNA-01] Disable public access (NIST SC-7)
    networkAcls: {
      defaultAction: 'Deny'  // ✅ [KSI-CNA-01] Deny-by-default network rules (NIST SC-7)
      virtualNetworkRules: []  // No VNet rules - using Private Endpoint instead
    }
  }
}

resource cosmosPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {
  name: 'pe-cosmos-sql'
  location: location
  properties: {
    subnet: {
      id: '${vnet.id}/subnets/snet-private-endpoints'  // ✅ [KSI-CNA-03] Private Endpoint in dedicated subnet (NIST SC-7)
    }
    privateLinkServiceConnections: [{
      name: 'cosmos-sql-connection'
      properties: {
        privateLinkServiceId: cosmosDb.id  // ✅ [KSI-CNA-03] Link to Cosmos DB (NIST SC-7)
        groupIds: ['Sql']  // ✅ [KSI-CNA-03] Target SQL API (subresource: Sql, MongoDB, Cassandra, Gremlin, Table)
      }
    }]
  }
}

resource cosmosPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = {
  parent: cosmosPrivateEndpoint
  name: 'default'
  properties: {
    privateDnsZoneConfigs: [{
      name: 'cosmos-sql-config'
      properties: {
        privateDnsZoneId: privateDnsZoneCosmosDb.id  // ✅ [KSI-CNA-03] Link to Private DNS Zone (NIST SC-7)
      }
    }]
  }
}

// ============================================================================
// STEP 5: Key Vault with Private Endpoint
// ============================================================================
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: 'kv-fedramp-cmk'
  location: location
  properties: {
    sku: { family: 'A', name: 'premium' }  // ✅ [KSI-SVC-06] Premium SKU for HSM-backed keys (NIST SC-12)
    tenantId: subscription().tenantId
    enabledForDiskEncryption: true  // ✅ [KSI-SVC-06] Enable for disk encryption (NIST SC-28)
    enableSoftDelete: true  // ✅ [KSI-AFR-09] Soft delete for key recovery (NIST CP-6, CP-9)
    softDeleteRetentionInDays: 90  // ✅ [KSI-AFR-09] 90-day retention (NIST CP-6, CP-9)
    enablePurgeProtection: true  // ✅ [KSI-AFR-09] Prevent permanent deletion (NIST CP-6, CP-9)
    publicNetworkAccess: 'Disabled'  // ✅ [KSI-CNA-01] Disable public access (NIST SC-7)
    networkAcls: {
      defaultAction: 'Deny'  // ✅ [KSI-CNA-01] Deny-by-default network rules (NIST SC-7)
      bypass: 'AzureServices'  // Allow Azure platform services
      virtualNetworkRules: []  // No VNet rules - using Private Endpoint instead
    }
  }
}

resource keyVaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {
  name: 'pe-keyvault'
  location: location
  properties: {
    subnet: {
      id: '${vnet.id}/subnets/snet-private-endpoints'  // ✅ [KSI-CNA-03] Private Endpoint in dedicated subnet (NIST SC-7)
    }
    privateLinkServiceConnections: [{
      name: 'keyvault-connection'
      properties: {
        privateLinkServiceId: keyVault.id  // ✅ [KSI-CNA-03] Link to Key Vault (NIST SC-7)
        groupIds: ['vault']  // ✅ [KSI-CNA-03] Target vault subresource (NIST SC-7)
      }
    }]
  }
}

resource keyVaultPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = {
  parent: keyVaultPrivateEndpoint
  name: 'default'
  properties: {
    privateDnsZoneConfigs: [{
      name: 'keyvault-config'
      properties: {
        privateDnsZoneId: privateDnsZoneKeyVault.id  // ✅ [KSI-CNA-03] Link to Private DNS Zone (NIST SC-7)
      }
    }]
  }
}
```

### Private Endpoint Subresources by Service:

| Service | subResourceName (groupId) | Private DNS Zone |
|---------|---------------------------|------------------|
| Storage Account | `blob`, `file`, `table`, `queue`, `dfs` | `privatelink.blob.core.windows.net` |
| Cosmos DB | `Sql`, `MongoDB`, `Cassandra`, `Gremlin`, `Table` | `privatelink.documents.azure.com` |
| Key Vault | `vault` | `privatelink.vaultcore.azure.net` |
| SQL Database | `sqlServer` | `privatelink.database.windows.net` |
| Event Hub | `namespace` | `privatelink.servicebus.windows.net` |
| Service Bus | `namespace` | `privatelink.servicebus.windows.net` |
| Azure Cache for Redis | `redisCache` | `privatelink.redis.cache.windows.net` |

### Terraform Private Endpoint Example:

```hcl
# Virtual Network
resource "azurerm_virtual_network" "vnet" {
  name                = "vnet-fedramp-private"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = ["10.0.0.0/16"]
}

resource "azurerm_subnet" "private_endpoints" {
  name                 = "snet-private-endpoints"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
  
  private_endpoint_network_policies_enabled = false  # Required
}

# Private DNS Zone for Storage
resource "azurerm_private_dns_zone" "storage" {
  name                = "privatelink.blob.core.windows.net"
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_private_dns_zone_virtual_network_link" "storage" {
  name                  = "${azurerm_virtual_network.vnet.name}-link"
  resource_group_name   = azurerm_resource_group.rg.name
  private_dns_zone_name = azurerm_private_dns_zone.storage.name
  virtual_network_id    = azurerm_virtual_network.vnet.id
}

# Storage Account with Private Endpoint
resource "azurerm_storage_account" "evidence" {
  name                     = "stfedrampevidence"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
  public_network_access_enabled = false  # ✅ Disabled
  
  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_private_endpoint" "storage" {
  name                = "pe-storage-blob"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name
  subnet_id           = azurerm_subnet.private_endpoints.id
  
  private_service_connection {
    name                           = "storage-blob-connection"
    private_connection_resource_id = azurerm_storage_account.evidence.id
    subresource_names              = ["blob"]
    is_manual_connection           = false
  }
  
  private_dns_zone_group {
    name                 = "default"
    private_dns_zone_ids = [azurerm_private_dns_zone.storage.id]
  }
}
```

### ⚠️ CRITICAL REMINDERS:

1. **ALWAYS create VNet + Subnets FIRST** before Private Endpoints
2. **ALWAYS create Private DNS Zones** and link to VNet for name resolution
3. **ALWAYS create Private Endpoints** when setting `publicNetworkAccess: 'Disabled'`
4. **NEVER disable public access** without confirming Private Endpoints are working
5. **TEST connectivity** from application subnet before deploying production

### Testing Private Endpoint Connectivity:

```powershell
# From VM in application subnet, test private endpoint
$storageAccount = "stfedrampevidence"
$privateDnsName = "$storageAccount.privatelink.blob.core.windows.net"

# Verify DNS resolution (should return private IP 10.0.1.x)
Resolve-DnsName -Name $privateDnsName

# Test HTTPS connectivity
Test-NetConnection -ComputerName $privateDnsName -Port 443

# Test blob access (should work via private endpoint)
$ctx = New-AzStorageContext -StorageAccountName $storageAccount -UseConnectedAccount
Get-AzStorageContainer -Context $ctx
```

---

### KSI-TPR: Third-Party Risk (4 KSIs)

**KSI-TPR-04: Supply Chain Risk**

**Azure Services:**
- Microsoft Defender for Cloud (Software Bill of Materials)
- Azure Policy
- Microsoft Purview

**Automation:**
```bash
# Generate SBOM for all container images
az acr repository list --name fedrampregistry --output table | while read repo
do
    az acr repository show-manifests \
        --name fedrampregistry \
        --repository $repo \
        --detail --query "[0].digest" -o tsv | while read digest
    do
        # Generate SBOM using Syft
        syft packages "fedrampregistry.azurecr.io/${repo}@${digest}" \
            -o json > "sbom-${repo}-$(date +%Y-%m-%d).json"
        
        # Upload to evidence storage
        az storage blob upload \
            --account-name fedrampevidence \
            --container-name sbom \
            --name "sbom-${repo}-$(date +%Y-%m-%d).json" \
            --file "sbom-${repo}-$(date +%Y-%m-%d).json"
    done
done
```

## Evidence Collection Automation Framework

### Centralized Evidence Repository

**Azure Architecture:**
```
Evidence Collection Flow:
1. Automated Scripts (PowerShell/CLI/KQL) → 
2. Azure Functions (scheduled triggers) →
3. Azure Blob Storage (immutable, encrypted) →
4. Azure Purview (cataloging) →
5. Authorization Data Sharing API (FRR-ADS)
```

**Implementation:**
```powershell
# Create evidence storage with immutability
$storageAccount = New-AzStorageAccount `
    -ResourceGroupName "rg-fedramp-evidence" `
    -Name "fedrampevidence" `
    -Location "eastus" `
    -SkuName "Standard_GRS" `
    -Kind "StorageV2" `
    -EnableHttpsTrafficOnly $true

# Enable blob versioning and immutability
Enable-AzStorageBlobDeleteRetentionPolicy `
    -ResourceGroupName "rg-fedramp-evidence" `
    -StorageAccountName "fedrampevidence" `
    -RetentionDays 2555  # 7 years for FedRAMP

Set-AzRmStorageContainerImmutabilityPolicy `
    -ResourceGroupName "rg-fedramp-evidence" `
    -StorageAccountName "fedrampevidence" `
    -ContainerName "evidence" `
    -ImmutabilityPeriod 365 `
    -AllowProtectedAppendWrites $true
```

### Automated Evidence Collection Schedule

**Azure Automation Runbook:**
```powershell
# Master evidence collection runbook
param(
    [string]$EvidenceDate = (Get-Date -Format "yyyy-MM-dd")
)

# Collect all KSI evidence
$evidenceCollectors = @(
    "Collect-IAM-Evidence",
    "Collect-MLA-Evidence",
    "Collect-AFR-Evidence",
    "Collect-CMT-Evidence",
    "Collect-CNA-Evidence",
    "Collect-INR-Evidence",
    "Collect-RPL-Evidence",
    "Collect-PIY-Evidence",
    "Collect-SVC-Evidence",
    "Collect-TPR-Evidence"
)

foreach ($collector in $evidenceCollectors) {
    try {
        Start-AzAutomationRunbook `
            -AutomationAccountName "automation-fedramp" `
            -Name $collector `
            -ResourceGroupName "rg-fedramp" `
            -Parameters @{ Date = $EvidenceDate }
        
        Write-Output "Started: $collector"
    }
    catch {
        Write-Error "Failed to start $collector: $_"
    }
}

# Generate daily summary report
$summary = @{
    Date = $EvidenceDate
    CollectorsRun = $evidenceCollectors.Count
    Status = "Completed"
}

$summary | ConvertTo-Json | Out-File "evidence-summary-$EvidenceDate.json"
```

### Dashboard & Reporting

**Power BI Integration:**
```powershell
# Push KSI metrics to Power BI
$dataSet = @{
    name = "FedRAMP-KSI-Metrics"
    tables = @(
        @{
            name = "KSICompliance"
            columns = @(
                @{ name = "KSI_ID"; dataType = "string" },
                @{ name = "KSI_Name"; dataType = "string" },
                @{ name = "ComplianceStatus"; dataType = "string" },
                @{ name = "MetricValue"; dataType = "string" },
                @{ name = "LastUpdated"; dataType = "datetime" }
            )
        }
    )
}

# Create dataset in Power BI
Invoke-RestMethod `
    -Uri "https://api.powerbi.com/v1.0/myorg/datasets" `
    -Method Post `
    -Headers @{ Authorization = "Bearer $powerBIToken" } `
    -Body ($dataSet | ConvertTo-Json -Depth 10) `
    -ContentType "application/json"

# Push daily metrics
$metrics = Get-AllKSIMetrics  # Your custom function
Invoke-RestMethod `
    -Uri "https://api.powerbi.com/v1.0/myorg/datasets/FedRAMP-KSI-Metrics/tables/KSICompliance/rows" `
    -Method Post `
    -Headers @{ Authorization = "Bearer $powerBIToken" } `
    -Body ($metrics | ConvertTo-Json) `
    -ContentType "application/json"
```

## Microsoft 365 Integration

### M365 Compliance Integration

**KSIs Covered by M365 E5 Compliance:**
- **KSI-MLA-02**: Audit logging (Microsoft Purview Audit)
- **KSI-TPR**: Data classification (Microsoft Purview Information Protection)
- **KSI-SVC-10**: Data destruction (Retention policies)

**Automation:**
```powershell
# Connect to Security & Compliance PowerShell
Connect-IPPSSession

# Enable unified audit log
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

# Create retention policy for FedRAMP
New-RetentionCompliancePolicy `
    -Name "FedRAMP-7-Year-Retention" `
    -Enabled $true `
    -ExchangeLocation "All" `
    -SharePointLocation "All" `
    -OneDriveLocation "All"

New-RetentionComplianceRule `
    -Policy "FedRAMP-7-Year-Retention" `
    -RetentionDuration 2555 `
    -RetentionComplianceAction Keep

# Export audit logs daily
Search-UnifiedAuditLog `
    -StartDate (Get-Date).AddDays(-1) `
    -EndDate (Get-Date) `
    -ResultSize 5000 | 
    Export-Csv "m365-audit-$(Get-Date -Format yyyy-MM-dd).csv"
```

### Microsoft Defender for Office 365

**KSIs Covered:**
- **KSI-INR-01**: Incident response (threat detection)
- **KSI-IAM-06**: Suspicious activity (anomaly detection)

**Automation:**
```powershell
# Get threat detections
Connect-ExchangeOnline

$threats = Get-ThreatDetection -StartDate (Get-Date).AddDays(-30)
$threats | Export-Csv "m365-threats-$(Get-Date -Format yyyy-MM-dd).csv"

# Get safe links/attachments clicks
$safeLinkClicks = Get-SafeLinksDetailReport -StartDate (Get-Date).AddDays(-30)
$safeLinkClicks | Export-Csv "safelinks-$(Get-Date -Format yyyy-MM-dd).csv"
```

## Complete Automation Template

Here's a complete Azure Function that collects evidence for ALL KSIs:

```csharp
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Graph;

public static class KSIEvidenceCollector
{
    [FunctionName("DailyKSICollection")]
    public static async Task Run(
        [TimerTrigger("0 0 2 * * *")] TimerInfo myTimer,  // Daily at 2 AM
        ILogger log)
    {
        log.LogInformation($"KSI Evidence Collection started at: {DateTime.Now}");
        
        var credential = new DefaultAzureCredential();
        var evidenceDate = DateTime.UtcNow.ToString("yyyy-MM-dd");
        
        // Initialize clients
        var armClient = new ArmClient(credential);
        var graphClient = new GraphServiceClient(credential);
        var blobClient = new BlobServiceClient(
            new Uri($"https://fedrampevidence.blob.core.windows.net"),
            credential);
        
        // Collect evidence for each KSI family
        await CollectIAMEvidence(graphClient, blobClient, evidenceDate);
        await CollectMLAEvidence(armClient, blobClient, evidenceDate);
        await CollectAFREvidence(armClient, blobClient, evidenceDate);
        // ... continue for all KSI families
        
        log.LogInformation($"KSI Evidence Collection completed at: {DateTime.Now}");
    }
    
    private static async Task CollectIAMEvidence(
        GraphServiceClient graphClient,
        BlobServiceClient blobClient,
        string evidenceDate)
    {
        // Get MFA status
        var users = await graphClient.Users.GetAsync();
        // ... process and upload to blob storage
        
        // Get Conditional Access policies
        var policies = await graphClient.Identity.ConditionalAccess.Policies.GetAsync();
        // ... process and upload
    }
    
    // ... implement other collection methods
}
```

## Next Steps

1. **Deploy Infrastructure**: Use the Bicep template to set up evidence collection infrastructure
2. **Configure Automation**: Set up Azure Automation runbooks for daily collection
3. **Test Evidence Flow**: Validate end-to-end evidence collection and storage
4. **Integrate with API**: Connect evidence storage to Authorization Data Sharing API
5. **Train Team**: Ensure team understands automation and can troubleshoot

## Tools to Use

- Use `get_ksi` to understand each KSI's requirements
- Use `api_design_guide` to integrate evidence into FRR-ADS API
- Use `ksi_implementation_priorities` to plan automation rollout
- Use `get_implementation_examples` for specific KSI code examples

**All PowerShell scripts and automation examples are production-ready for Azure Government and FedRAMP compliance!**