## Bicep Template

```bicep
// evidence_single_ksi.bicep - Single-KSI Evidence Collection Architecture
// Production architecture for one KSI with monitoring, security, and high availability
// Suitable for: Production deployments, critical KSIs, enterprise environments
//
// FedRAMP 20x KSI Alignment:
// - KSI-MLA-01: Log aggregation and centralized logging
// - KSI-MLA-02: Log retention with immutability
// - KSI-MLA-05: Audit logging and tamper detection
// - KSI-CED-01: Continuous evidence collection
// - KSI-IAM-05: Managed identities for service accounts
// - KSI-INR-02: Incident detection and alerting
// - FRR-ADS: Authorization Data Sharing (machine-readable evidence)
//
// Architecture Components:
// 1. Log Analytics Workspace with Sentinel integration
// 2. Storage Account with immutability policies
// 3. Azure Functions (Premium plan for VNet integration)
// 4. Event Grid with dead-letter queue
// 5. Key Vault for secrets management
// 6. Application Insights for monitoring
// 7. Alert rules for operational awareness

targetScope = 'resourceGroup'

@description('Location for all resources')
param location string = resourceGroup().location

@description('Unique suffix for resource naming')
param nameSuffix string = uniqueString(resourceGroup().id)

@description('KSI identifier (e.g., KSI-IAM-01)')
param ksiId string

@description('Log retention in days (90-730)')
@minValue(90)
@maxValue(730)
param logRetentionDays int = 365

@description('Evidence retention in days (1825-2555, 5-7 years)')
@minValue(1825)
@maxValue(2555)
param evidenceRetentionDays int = 2555

@description('Enable Microsoft Sentinel')
param enableSentinel bool = true

@description('Email for operational alerts')
param alertEmail string

// ============================================================================
// Log Analytics Workspace - Centralized Logging (KSI-MLA-01, KSI-MLA-02)
// ============================================================================

// Supports: KSI-MLA-01 (log aggregation), KSI-MLA-02 (retention policies)
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
  name: 'law-${ksiId}-${nameSuffix}'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
    retentionInDays: logRetentionDays
    features: {
      enableLogAccessUsingOnlyResourcePermissions: true
      immediatePurgeDataOn30Days: false // Prevent accidental data loss
    }
    publicNetworkAccessForIngestion: 'Enabled'
    publicNetworkAccessForQuery: 'Enabled'
  }
}

// Microsoft Sentinel (Security Information and Event Management)
resource sentinel 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = if (enableSentinel) {
  name: 'SecurityInsights(${logAnalytics.name})'
  location: location
  plan: {
    name: 'SecurityInsights(${logAnalytics.name})'
    product: 'OMSGallery/SecurityInsights'
    publisher: 'Microsoft'
    promotionCode: ''
  }
  properties: {
    workspaceResourceId: logAnalytics.id
  }
}

// ============================================================================
// Storage Account - Evidence Storage with Immutability (KSI-CED-01, KSI-MLA-05)
// ============================================================================

// Supports: KSI-CED-01 (continuous evidence collection), KSI-MLA-05 (tamper detection)
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'st${replace(ksiId, '-', '')}${nameSuffix}'
  location: location
  sku: {
    name: 'Standard_GRS' // Geo-redundant for disaster recovery
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Cool' // Cost optimization for long-term storage
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
    encryption: {
      services: {
        blob: {
          enabled: true
          keyType: 'Account'
        }
      }
      keySource: 'Microsoft.Storage'
      requireInfrastructureEncryption: true // Double encryption
    }
    networkAcls: {
      defaultAction: 'Allow' // Change to 'Deny' with VNet rules for enhanced security
      bypass: 'AzureServices'
    }
  }
}

resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
  parent: storageAccount
  name: 'default'
  properties: {
    deleteRetentionPolicy: {
      enabled: true
      days: 30 // Soft delete protection
    }
    containerDeleteRetentionPolicy: {
      enabled: true
      days: 30
    }
    changeFeed: {
      enabled: true // Audit trail for blob changes
      retentionInDays: 90
    }
    isVersioningEnabled: true // Version control for evidence
  }
}

// Evidence container with time-based immutability
resource evidenceContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
  parent: blobService
  name: 'evidence-${toLower(ksiId)}'
  properties: {
    publicAccess: 'None'
    metadata: {
      purpose: 'FedRAMP 20x evidence storage'
      ksi: ksiId
      retention: '${evidenceRetentionDays} days'
      complianceLevel: 'FedRAMP-High'
    }
  }
}

// Immutability policy (WORM storage)
resource immutabilityPolicy 'Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies@2023-01-01' = {
  parent: evidenceContainer
  name: 'default'
  properties: {
    immutabilityPeriodSinceCreationInDays: evidenceRetentionDays
    allowProtectedAppendWrites: true // Allow append operations for log files
  }
}

// Lifecycle management
resource lifecyclePolicy 'Microsoft.Storage/storageAccounts/managementPolicies@2023-01-01' = {
  parent: storageAccount
  name: 'default'
  properties: {
    policy: {
      rules: [
        {
          enabled: true
          name: 'MoveToArchive'
          type: 'Lifecycle'
          definition: {
            actions: {
              baseBlob: {
                tierToArchive: {
                  daysAfterModificationGreaterThan: 180 // Move to archive after 6 months
                }
                delete: {
                  daysAfterModificationGreaterThan: evidenceRetentionDays
                }
              }
            }
            filters: {
              blobTypes: ['blockBlob']
              prefixMatch: ['evidence-${toLower(ksiId)}/']
            }
          }
        }
      ]
    }
  }
}

// ============================================================================
// Key Vault - Secrets Management (KSI-IAM-05)
// ============================================================================

// Supports: KSI-IAM-05 (centralized secrets management with RBAC)
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: 'kv-${ksiId}-${nameSuffix}'
  location: location
  properties: {
    sku: {
      family: 'A'
      name: 'standard'
    }
    tenantId: subscription().tenantId
    enableRbacAuthorization: true // Use RBAC instead of access policies
    enableSoftDelete: true
    softDeleteRetentionInDays: 90
    enablePurgeProtection: true
    networkAcls: {
      defaultAction: 'Allow'
      bypass: 'AzureServices'
    }
  }
}

// ============================================================================
// Managed Identity - Secure Authentication (KSI-IAM-05)
// ============================================================================

// Supports: KSI-IAM-05 (service accounts use managed identities, not credentials)
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: 'id-${ksiId}-collector-${nameSuffix}'
  location: location
}

// Storage role assignments
resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storageAccount.id, managedIdentity.id, 'StorageBlobDataContributor')
  scope: storageAccount
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
    principalId: managedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource logAnalyticsRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(logAnalytics.id, managedIdentity.id, 'LogAnalyticsReader')
  scope: logAnalytics
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')
    principalId: managedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource keyVaultRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(keyVault.id, managedIdentity.id, 'KeyVaultSecretsUser')
  scope: keyVault
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
    principalId: managedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

// ============================================================================
// Application Insights - Monitoring (KSI-MLA-05, KSI-INR-02)
// ============================================================================

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: 'appi-${ksiId}-${nameSuffix}'
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logAnalytics.id
    RetentionInDays: logRetentionDays
  }
}

// ============================================================================
// Azure Function - Evidence Collection (KSI-CED-01)
// ============================================================================

resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: 'asp-${ksiId}-${nameSuffix}'
  location: location
  sku: {
    name: 'EP1' // Elastic Premium for VNet integration and better performance
    tier: 'ElasticPremium'
  }
  properties: {
    reserved: true // Linux
    maximumElasticWorkerCount: 20
  }
}

resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
  name: 'func-${ksiId}-${nameSuffix}'
  location: location
  kind: 'functionapp,linux'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${managedIdentity.id}': {}
    }
  }
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      linuxFxVersion: 'PYTHON|3.11'
      alwaysOn: true // Keep function warm
      appSettings: [
        {
          name: 'AzureWebJobsStorage'
          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
        }
        {
          name: 'FUNCTIONS_EXTENSION_VERSION'
          value: '~4'
        }
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'python'
        }
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: appInsights.properties.InstrumentationKey
        }
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: appInsights.properties.ConnectionString
        }
        {
          name: 'LOG_ANALYTICS_WORKSPACE_ID'
          value: logAnalytics.properties.customerId
        }
        {
          name: 'STORAGE_ACCOUNT_NAME'
          value: storageAccount.name
        }
        {
          name: 'EVIDENCE_CONTAINER_NAME'
          value: evidenceContainer.name
        }
        {
          name: 'KEY_VAULT_NAME'
          value: keyVault.name
        }
        {
          name: 'MANAGED_IDENTITY_CLIENT_ID'
          value: managedIdentity.properties.clientId
        }
        {
          name: 'KSI_ID'
          value: ksiId
        }
      ]
      ftpsState: 'Disabled'
      minTlsVersion: '1.2'
      http20Enabled: true
    }
  }
}

// ============================================================================
// Event Grid - Evidence Notifications (KSI-MLA-05)
// ============================================================================

resource eventGridTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' = {
  name: 'evgt-${ksiId}-${nameSuffix}'
  location: location
  properties: {
    source: storageAccount.id
    topicType: 'Microsoft.Storage.StorageAccounts'
  }
}

// Dead-letter container for failed events
resource deadLetterContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
  parent: blobService
  name: 'deadletter-${toLower(ksiId)}'
  properties: {
    publicAccess: 'None'
  }
}

resource eventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2023-12-15-preview' = {
  parent: eventGridTopic
  name: 'evidence-uploaded'
  properties: {
    destination: {
      endpointType: 'WebHook'
      properties: {
        endpointUrl: 'https://${functionApp.properties.defaultHostName}/api/evidence-notification'
        maxEventsPerBatch: 10
        preferredBatchSizeInKilobytes: 64
      }
    }
    filter: {
      includedEventTypes: [
        'Microsoft.Storage.BlobCreated'
      ]
      subjectBeginsWith: '/blobServices/default/containers/${evidenceContainer.name}/'
    }
    deadLetterDestination: {
      endpointType: 'StorageBlob'
      properties: {
        resourceId: storageAccount.id
        blobContainerName: deadLetterContainer.name
      }
    }
    retryPolicy: {
      maxDeliveryAttempts: 30
      eventTimeToLiveInMinutes: 1440 // 24 hours
    }
    eventDeliverySchema: 'EventGridSchema'
  }
}

// ============================================================================
// Alert Rules - Operational Awareness (KSI-INR-02, KSI-MLA-05)
// ============================================================================

// Action group for alerts
resource actionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
  name: 'ag-${ksiId}-${nameSuffix}'
  location: 'global'
  properties: {
    groupShortName: substring(ksiId, 0, 12)
    enabled: true
    emailReceivers: [
      {
        name: 'EmailAdmin'
        emailAddress: alertEmail
        useCommonAlertSchema: true
      }
    ]
  }
}

// Alert: Function execution failures
resource functionFailureAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
  name: 'alert-${ksiId}-function-failures'
  location: 'global'
  properties: {
    description: 'Alert when evidence collection function fails'
    severity: 2
    enabled: true
    scopes: [
      functionApp.id
    ]
    evaluationFrequency: 'PT5M'
    windowSize: 'PT15M'
    criteria: {
      'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'
      allOf: [
        {
          name: 'FunctionErrors'
          metricName: 'FunctionExecutionCount'
          dimensions: [
            {
              name: 'Status'
              operator: 'Include'
              values: ['Failed']
            }
          ]
          operator: 'GreaterThan'
          threshold: 3
          timeAggregation: 'Total'
        }
      ]
    }
    actions: [
      {
        actionGroupId: actionGroup.id
      }
    ]
  }
}

// Alert: Storage account availability
resource storageAvailabilityAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
  name: 'alert-${ksiId}-storage-availability'
  location: 'global'
  properties: {
    description: 'Alert when evidence storage availability drops'
    severity: 1
    enabled: true
    scopes: [
      storageAccount.id
    ]
    evaluationFrequency: 'PT5M'
    windowSize: 'PT15M'
    criteria: {
      'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'
      allOf: [
        {
          name: 'Availability'
          metricName: 'Availability'
          operator: 'LessThan'
          threshold: 99
          timeAggregation: 'Average'
        }
      ]
    }
    actions: [
      {
        actionGroupId: actionGroup.id
      }
    ]
  }
}

// ============================================================================
// Diagnostic Settings - Comprehensive Audit Logging (KSI-MLA-05)
// ============================================================================

resource storageDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: storageAccount
  name: 'storage-diagnostics'
  properties: {
    workspaceId: logAnalytics.id
    logs: [
      {
        category: 'StorageRead'
        enabled: true
      }
      {
        category: 'StorageWrite'
        enabled: true
      }
      {
        category: 'StorageDelete'
        enabled: true
      }
    ]
    metrics: [
      {
        category: 'Transaction'
        enabled: true
      }
      {
        category: 'Capacity'
        enabled: true
      }
    ]
  }
}

resource functionDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: functionApp
  name: 'function-diagnostics'
  properties: {
    workspaceId: logAnalytics.id
    logs: [
      {
        category: 'FunctionAppLogs'
        enabled: true
      }
    ]
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
      }
    ]
  }
}

resource keyVaultDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: keyVault
  name: 'keyvault-diagnostics'
  properties: {
    workspaceId: logAnalytics.id
    logs: [
      {
        category: 'AuditEvent'
        enabled: true
      }
      {
        category: 'AzurePolicyEvaluationDetails'
        enabled: true
      }
    ]
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
      }
    ]
  }
}

// ============================================================================
// Outputs
// ============================================================================

output logAnalyticsWorkspaceId string = logAnalytics.id
output storageAccountName string = storageAccount.name
output evidenceContainerName string = evidenceContainer.name
output keyVaultName string = keyVault.name
output functionAppName string = functionApp.name
output appInsightsName string = appInsights.name
output managedIdentityId string = managedIdentity.id
output managedIdentityClientId string = managedIdentity.properties.clientId
```

## Deployment Instructions

```bash
# Create resource group
az group create --name rg-evidence-ksi-iam-01 --location eastus

# Deploy template
az deployment group create \
  --resource-group rg-evidence-ksi-iam-01 \
  --template-file evidence_single_ksi.bicep \
  --parameters \
    ksiId='KSI-IAM-01' \
    logRetentionDays=365 \
    evidenceRetentionDays=2555 \
    enableSentinel=true \
    alertEmail='security@example.com'
```

## Usage Notes

**Purpose:** Production-grade evidence collection for a single critical KSI.

**Scope:** Suitable for enterprise deployments, regulated environments, critical KSIs.

**Features:**
- Geo-redundant storage for disaster recovery
- Immutability policies (WORM storage)
- Microsoft Sentinel integration
- Comprehensive alerting
- Key Vault for secrets
- Application Insights monitoring
- Dead-letter queue for failed events
- Soft delete and versioning

**Scaling:** Deploy one instance per critical KSI, or migrate to category architecture for 5+ KSIs.
