Module 34 Advanced 5 labs

Enterprise Governance, Compliance & Audit Evidence

Xây dựng governance as code với OPA/Azure Policy, tự động hóa compliance scan (CIS Benchmark), thu thập audit log evidence, phát hiện IaC drift, và mapping control sang SOC 2 / ISO 27001 — tất cả tích hợp vào DevSecOps pipeline.

Công cụ thực hành OPA/Conftest, Azure Policy, Azure CLI, Terraform, Checkov, Azure Activity Log
Nền tảng Azure (Policy, Monitor, Defender for Cloud), GitHub Actions, Terraform Cloud
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Governance as Code — tại sao cần?

Trong enterprise DevSecOps, governance truyền thống dựa vào checklist thủ công và Change Advisory Board (CAB) — chậm, dễ bỏ sót, không scale. Governance as Code (GaC) mã hóa policy thành code, kiểm tra trước khi resource được tạo (shift-left). Lợi ích: repeatability (mỗi deploy đều qua cùng guard rail), auditability (policy trong git = lịch sử thay đổi), self-service (team tự deploy mà không cần CAB nếu pass policy).

Hai công cụ chính

  • OPA (Open Policy Agent) + Rego: Policy engine cloud-agnostic, dùng cho Kubernetes admission control, Terraform plan validation, API authorization. Rego là ngôn ngữ khai báo để viết rule.
  • Azure Policy: Managed service của Azure, enforce policy ở control plane level (ARM). Built-in initiatives như CIS Azure Benchmark, PCI-DSS. Assign cho subscription/management group → tự động remediate non-compliant resource.

1.2. Compliance Scan — CIS Benchmark

CIS (Center for Internet Security) Benchmarks là tập cấu hình bảo mật được cộng đồng đồng thuận cho từng platform (Azure, AWS, Kubernetes, Linux, Docker). Ví dụ CIS Azure Benchmark v2.0 có 150+ control. Công cụ scan tự động:

1.3. Audit Log Evidence — chứng minh cho auditor

Audit evidence là proof rằng control đang hoạt động. Ba loại log quan trọng trong Azure:

1.4. IaC Drift Detection

Drift xảy ra khi ai đó thay đổi resource thực tế trực tiếp (portal, CLI) mà không qua Terraform/code — hạ tầng thực lệch khỏi state file. Drift phá vỡ principle "hạ tầng là code" và tạo compliance risk. Phát hiện drift:

1.5. Control Mapping — SOC 2 & ISO 27001

Technical ControlSOC 2 TSCISO 27001
Azure Policy "Deny" non-compliant resourceCC6.1 Logical AccessA.9.4 System Access Control
Activity Log retained ≥ 1 nămCC7.2 MonitoringA.12.4 Logging & Monitoring
IaC drift detection + alertCC8.1 Change ManagementA.12.1 Operational Procedures
Checkov scan trong CI pipelineCC6.8 Malicious SoftwareA.14.2 Security in Dev Lifecycle
Defender for Cloud Secure ScoreCC9.2 Risk MonitoringA.18.2 Compliance Review

Mapping này cho phép team DevOps chứng minh với auditor rằng mỗi technical control tương ứng với requirement cụ thể trong framework — không cần audit riêng cho từng framework.

2. Thực hành (Labs)

LAB-001

Policy as Code với OPA/Conftest: validate Terraform plan

OPA · Conftest · Terraform · Rego

🎯 Mục tiêu: Viết Rego policy ngăn deploy Azure Storage Account không có HTTPS-only và encryption, tích hợp vào CI pipeline qua Conftest.

🧰 Công cụ / nền tảng: Conftest, OPA, Terraform CLI, Git.

📦 Chuẩn bị: Cài Conftest (winget install Styra.Conftest hoặc download binary); cài Terraform CLI.

▶️ Các bước:

# BƯỚC 1: Tạo thư mục lab
mkdir governance-lab1 && cd governance-lab1
mkdir policy

# BƯỚC 2: Viết Terraform config cố ý vi phạm policy
cat > main.tf <<'EOF'
terraform {
  required_providers {
    azurerm = { source = "hashicorp/azurerm", version = "~> 3.0" }
  }
}

resource "azurerm_storage_account" "bad_example" {
  name                     = "stbadexample001"
  resource_group_name      = "rg-test"
  location                 = "southeastasia"
  account_tier             = "Standard"
  account_replication_type = "LRS"

  # Vi phạm 1: enable_https_traffic_only = false (default false trong v3)
  enable_https_traffic_only = false

  # Vi phạm 2: min_tls_version không phải TLS1_2
  min_tls_version = "TLS1_0"

  # Vi phạm 3: blob_properties không có delete retention
}
EOF

# BƯỚC 3: Viết Rego policy
cat > policy/azure_storage.rego <<'EOF'
package main

import future.keywords.if
import future.keywords.in

# Deny storage account without HTTPS-only
deny contains msg if {
  resource := input.resource_changes[_]
  resource.type == "azurerm_storage_account"
  resource.change.after.enable_https_traffic_only == false
  msg := sprintf(
    "DENY [CC6.1/A.9.4] Storage account '%s' must have HTTPS-only enabled",
    [resource.address]
  )
}

# Deny storage account with TLS < 1.2
deny contains msg if {
  resource := input.resource_changes[_]
  resource.type == "azurerm_storage_account"
  not resource.change.after.min_tls_version == "TLS1_2"
  msg := sprintf(
    "DENY [CC6.8/A.14.2] Storage account '%s' must use min TLS 1.2 (got: %s)",
    [resource.address, resource.change.after.min_tls_version]
  )
}

# Warn storage account without delete retention
warn contains msg if {
  resource := input.resource_changes[_]
  resource.type == "azurerm_storage_account"
  not resource.change.after.blob_properties
  msg := sprintf(
    "WARN [CC7.2] Storage account '%s' has no blob delete retention configured",
    [resource.address]
  )
}
EOF

# BƯỚC 4: Generate Terraform plan JSON (không cần Azure creds — dùng mock provider)
terraform init -backend=false

# Tạo plan (sẽ lỗi provider credentials, nhưng ta dùng plan JSON mock)
# Cách đơn giản hơn: tạo plan JSON mẫu trực tiếp
cat > tfplan.json <<'EOF'
{
  "resource_changes": [
    {
      "address": "azurerm_storage_account.bad_example",
      "type": "azurerm_storage_account",
      "change": {
        "actions": ["create"],
        "after": {
          "name": "stbadexample001",
          "enable_https_traffic_only": false,
          "min_tls_version": "TLS1_0"
        }
      }
    }
  ]
}
EOF

# BƯỚC 5: Chạy Conftest validate
conftest test tfplan.json --policy policy/

# BƯỚC 6: Sửa vi phạm và test lại
cat > tfplan-fixed.json <<'EOF'
{
  "resource_changes": [
    {
      "address": "azurerm_storage_account.good_example",
      "type": "azurerm_storage_account",
      "change": {
        "actions": ["create"],
        "after": {
          "name": "stgoodexample001",
          "enable_https_traffic_only": true,
          "min_tls_version": "TLS1_2",
          "blob_properties": { "delete_retention_policy": { "days": 7 } }
        }
      }
    }
  ]
}
EOF

conftest test tfplan-fixed.json --policy policy/

✅ Kết quả mong đợi: Lần 1 (tfplan.json): Conftest in 2 dòng DENY màu đỏ (HTTPS + TLS) và 1 WARN — exit code 1 (pipeline sẽ fail). Lần 2 (tfplan-fixed.json): output 1 test, 0 failures — exit code 0 (pipeline pass).

🧹 Cleanup: cd .. && Remove-Item -Recurse governance-lab1.

LAB-002

CIS Compliance Scan với Checkov + Defender for Cloud

Checkov · Azure CLI · Defender for Cloud

🎯 Mục tiêu: Chạy Checkov scan IaC theo CIS Azure Benchmark, xuất báo cáo JSON/HTML, xem Secure Score trong Defender for Cloud.

🧰 Công cụ / nền tảng: Python/pip (Checkov), Azure CLI, Azure Portal (Defender for Cloud).

📦 Chuẩn bị: Python 3.8+ và pip; Azure subscription với Defender for Cloud enabled (free tier).

▶️ Các bước:

# BƯỚC 1: Cài Checkov
pip install checkov

# Kiểm tra version
checkov --version

# BƯỚC 2: Tạo Terraform file mẫu để scan
mkdir compliance-lab2 && cd compliance-lab2

cat > main.tf <<'EOF'
resource "azurerm_storage_account" "example" {
  name                     = "stexample001"
  resource_group_name      = "rg-test"
  location                 = "southeastasia"
  account_tier             = "Standard"
  account_replication_type = "LRS"
  # Thiếu: enable_https_traffic_only, min_tls_version, network_rules
}

resource "azurerm_key_vault" "example" {
  name                = "kv-example-001"
  location            = "southeastasia"
  resource_group_name = "rg-test"
  tenant_id           = "00000000-0000-0000-0000-000000000000"
  sku_name            = "standard"
  # Thiếu: purge_protection_enabled, soft_delete_retention_days
  # Thiếu: network_acls
}
EOF

# BƯỚC 3: Chạy Checkov scan với CIS Azure framework
checkov -d . --framework terraform --check CKV_AZURE_1,CKV_AZURE_3,CKV_AZURE_33,CKV_AZURE_35

# BƯỚC 4: Scan đầy đủ và xuất báo cáo JSON
checkov -d . --framework terraform `
  --output json `
  --output-file-path ./reports/

# BƯỚC 5: Scan theo CIS Azure benchmark benchmark (tất cả checks)
checkov -d . --framework terraform --compact

# Xem summary
Write-Host ""
Write-Host "=== Compliance Summary ==="
checkov -d . --framework terraform --quiet 2>&1 | Select-String "Passed|Failed|Skipped"

# BƯỚC 6: Xem Defender for Cloud Secure Score qua CLI
az security secure-score list --output table

# Xem chi tiết recommendations cho subscription
az security assessment list `
  --query "[?properties.status.code=='Unhealthy'].{Name:displayName, Severity:properties.metadata.severity}" `
  --output table `
  | Select-Object -First 20

# BƯỚC 7: Export compliance report từ Defender for Cloud
# Azure Portal → Defender for Cloud → Regulatory Compliance
# → CIS Azure Foundations Benchmark v2.0.0 → Download report (PDF/CSV)
# Hoặc via CLI:
az security regulatory-compliance-assessments list `
  --standard-name "CIS-Azure-Foundations-Benchmark-v2.0.0" `
  --control-name "1" `
  --output table 2>&1 | Select-Object -First 30

✅ Kết quả mong đợi: Checkov output: bảng PASS/FAIL cho từng check với mã CKV_AZURE_xxx, dòng code vi phạm, và link docs. Folder reports/ chứa file results_terraform.json. az security secure-score list hiển thị Secure Score % của subscription.

🧹 Cleanup: cd .. && Remove-Item -Recurse compliance-lab2. Defender for Cloud free tier không tốn phí — có thể giữ lại.

LAB-003

Audit Log Evidence: thu thập & query Azure Activity Log

Azure CLI · Log Analytics · KQL · PowerShell

🎯 Mục tiêu: Export Azure Activity Log, query ai tạo/xóa resource trong 30 ngày, export CSV làm audit evidence cho SOC 2 CC7.2.

🧰 Công cụ / nền tảng: Azure CLI, Log Analytics Workspace, KQL, PowerShell.

📦 Chuẩn bị: Azure CLI đã login; có Log Analytics Workspace (từ LAB-004 module trước hoặc tạo mới); Activity Log đã được diagnostic setting export sang workspace.

▶️ Các bước:

# BƯỚC 1: Xem Activity Log trực tiếp qua CLI (90 ngày gần đây)
$SUBSCRIPTION_ID = $(az account show --query id -o tsv)

# Lấy tất cả write/delete operations (loại bỏ read)
az monitor activity-log list `
  --offset 30d `
  --query "[?authorization.action != null] | [?contains(authorization.action, 'write') || contains(authorization.action, 'delete')] | [0:20].{
    Time:eventTimestamp,
    Caller:caller,
    Operation:operationName.localizedValue,
    Resource:resourceId,
    Status:status.value
  }" `
  --output table

# BƯỚC 2: Query chi tiết — ai tạo resource group?
az monitor activity-log list `
  --offset 30d `
  --query "[?operationName.value=='Microsoft.Resources/resourceGroups/write'].{
    Time:eventTimestamp,
    Caller:caller,
    ResourceGroup:resourceGroupName,
    Status:status.value,
    CorrelationId:correlationId
  }" `
  --output table

# BƯỚC 3: Export sang CSV cho auditor
$auditData = az monitor activity-log list `
  --offset 30d `
  --query "[?authorization.action != null && status.value=='Succeeded'].{
    EventTime:eventTimestamp,
    Caller:caller,
    OperationName:operationName.localizedValue,
    ResourceType:resourceType.localizedValue,
    ResourceGroup:resourceGroupName,
    Status:status.value,
    CorrelationId:correlationId
  }" `
  --output json | ConvertFrom-Json

$auditData | Export-Csv -Path "audit-evidence-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($auditData.Count) events to audit-evidence CSV"

# BƯỚC 4: Cấu hình Diagnostic Setting để export sang Log Analytics (lưu lâu dài)
$RG = "rg-governance-lab"
$LAW_ID = $(az monitor log-analytics workspace show `
  --resource-group $RG --workspace-name "law-aiops-lab" `
  --query id -o tsv)

az monitor diagnostic-settings create `
  --name "activity-log-to-law" `
  --resource "/subscriptions/$SUBSCRIPTION_ID" `
  --workspace $LAW_ID `
  --logs '[{"category": "Administrative", "enabled": true},
           {"category": "Security", "enabled": true},
           {"category": "Policy", "enabled": true},
           {"category": "Alert", "enabled": true}]'

# BƯỚC 5: KQL query trong Log Analytics — audit evidence report
$WORKSPACE_ID = $(az monitor log-analytics workspace show `
  --resource-group $RG --workspace-name "law-aiops-lab" `
  --query customerId -o tsv)

az monitor log-analytics query `
  --workspace $WORKSPACE_ID `
  --analytics-query @'
AzureActivity
| where TimeGenerated > ago(30d)
| where ActivityStatusValue == "Success"
| where OperationNameValue has_any ("write", "delete", "action")
| summarize
    OperationCount = count()
    by Caller, OperationNameValue, ResourceGroup, bin(TimeGenerated, 1d)
| sort by TimeGenerated desc, OperationCount desc
| take 50
'@ `
  --timespan P30D `
  --output json | ConvertFrom-Json | Select-Object -ExpandProperty tables | ForEach-Object { $_.rows } | Format-Table

✅ Kết quả mong đợi: File audit-evidence-YYYYMMDD.csv chứa danh sách event với Caller (UPN), OperationName, ResourceGroup, Timestamp. KQL query trong Log Analytics trả về bảng "ai làm gì trong 30 ngày". Diagnostic setting ở trạng thái Enabled — từ nay log được giữ theo retention policy của workspace (90 ngày–7 năm).

🧹 Cleanup: Remove-Item audit-evidence-*.csv sau khi kiểm tra. Diagnostic setting giữ lại để đảm bảo log liên tục.

LAB-004

IaC Drift Detection với Terraform & GitHub Actions

Terraform · GitHub Actions · Azure CLI

🎯 Mục tiêu: Tạo GitHub Actions workflow chạy terraform plan định kỳ, phát hiện drift và gửi alert qua Teams webhook.

🧰 Công cụ / nền tảng: Terraform, GitHub Actions, Azure CLI, Teams Webhook.

📦 Chuẩn bị: GitHub repo có Terraform code; Azure Service Principal với quyền Contributor; các secrets: ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_SUBSCRIPTION_ID, ARM_TENANT_ID, TEAMS_WEBHOOK_URL.

▶️ Các bước:

# BƯỚC 1: Tạo GitHub Actions workflow cho drift detection
mkdir -p .github/workflows

cat > .github/workflows/drift-detection.yml <<'EOF'
name: IaC Drift Detection

on:
  schedule:
    - cron: '0 */6 * * *'   # Mỗi 6 giờ
  workflow_dispatch:          # Cho phép chạy thủ công

jobs:
  drift-check:
    runs-on: ubuntu-latest
    env:
      ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
      ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
      ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
      ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "~1.7"

      - name: Terraform Init
        run: terraform init
        working-directory: ./infra

      - name: Terraform Plan (detect drift)
        id: plan
        run: |
          terraform plan -detailed-exitcode -out=tfplan.binary 2>&1 | tee plan_output.txt
          echo "exitcode=$?" >> $GITHUB_OUTPUT
        working-directory: ./infra
        continue-on-error: true

      - name: Parse drift result
        id: drift
        run: |
          EXIT_CODE="${{ steps.plan.outputs.exitcode }}"
          # Exit code: 0=no changes, 1=error, 2=changes detected (DRIFT!)
          if [ "$EXIT_CODE" == "2" ]; then
            echo "drift_detected=true" >> $GITHUB_OUTPUT
            CHANGES=$(grep -E "^  # |will be|must be" infra/plan_output.txt | head -20 | tr '\n' '|')
            echo "changes=$CHANGES" >> $GITHUB_OUTPUT
          else
            echo "drift_detected=false" >> $GITHUB_OUTPUT
          fi

      - name: Send Teams alert on drift
        if: steps.drift.outputs.drift_detected == 'true'
        run: |
          CHANGES="${{ steps.drift.outputs.changes }}"
          REPO="${{ github.repository }}"
          RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"

          curl -H "Content-Type: application/json" \
            -d "{
              \"@type\": \"MessageCard\",
              \"themeColor\": \"FF6600\",
              \"title\": \"⚠️ IaC Drift Detected — $REPO\",
              \"text\": \"Terraform state diverged from actual infrastructure. Manual changes may have been made outside pipeline.\",
              \"sections\": [{
                \"facts\": [
                  {\"name\": \"Repository\", \"value\": \"$REPO\"},
                  {\"name\": \"Changes\", \"value\": \"See run log\"},
                  {\"name\": \"Action\", \"value\": \"Review and re-apply or update Terraform code\"}
                ]
              }],
              \"potentialAction\": [{
                \"@type\": \"OpenUri\",
                \"name\": \"View Pipeline Run\",
                \"targets\": [{\"os\": \"default\", \"uri\": \"$RUN_URL\"}]
              }]
            }" \
            "${{ secrets.TEAMS_WEBHOOK_URL }}"

      - name: Comment on PR if drift
        if: steps.drift.outputs.drift_detected == 'true' && github.event_name == 'pull_request'
        run: |
          gh pr comment ${{ github.event.pull_request.number }} \
            --body "⚠️ **IaC Drift Detected**: Actual infrastructure differs from Terraform state. Review before merge." \
            --repo ${{ github.repository }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EOF

# BƯỚC 2: Commit workflow
git add .github/workflows/drift-detection.yml
git commit -m "feat: add terraform drift detection workflow"
git push

# BƯỚC 3: Trigger thủ công để test
gh workflow run drift-detection.yml
gh run list --workflow=drift-detection.yml --limit 5
gh run watch

# BƯỚC 4: Mô phỏng drift — tạo resource thủ công ngoài Terraform (Azure CLI)
# Sau đó chờ workflow chạy lần tiếp theo sẽ detect được diff
az storage account create \
  --name "stmanualcreate001" \
  --resource-group "rg-test" \
  --location "southeastasia" \
  --sku Standard_LRS
# Resource này KHÔNG có trong Terraform code → drift!

✅ Kết quả mong đợi: Workflow chạy thành công. Khi không có drift: pipeline pass, không gửi alert. Khi có drift (sau khi tạo resource thủ công): pipeline step "Terraform Plan" exit code 2 → Teams nhận card màu cam "IaC Drift Detected" với link pipeline. gh run list hiển thị run gần nhất với conclusion success hoặc failure tùy drift.

🧹 Cleanup: az storage account delete --name stmanualcreate001 --resource-group rg-test --yes. Xóa workflow nếu không cần: git rm .github/workflows/drift-detection.yml.

LAB-005

Control Mapping: tạo SOC 2 / ISO 27001 control matrix

PowerShell · Azure CLI · Excel/CSV

🎯 Mục tiêu: Tạo control matrix CSV mapping technical DevSecOps controls sang SOC 2 Trust Services Criteria và ISO 27001 Annex A — tài liệu sẵn sàng cho auditor.

🧰 Công cụ / nền tảng: PowerShell, Azure CLI, Excel hoặc bất kỳ spreadsheet tool nào.

📦 Chuẩn bị: Azure CLI đã login; đã hoàn thành LAB-001 đến LAB-004 (các control đã được implement).

▶️ Các bước:

# BƯỚC 1: Tạo control matrix (PowerShell)
$controls = @(
  [PSCustomObject]@{
    ControlID       = "CTL-001"
    TechnicalControl = "OPA/Conftest policy trong CI pipeline"
    Description     = "Mọi Terraform plan phải pass Rego policy trước khi apply"
    Implementation   = "conftest test tfplan.json --policy policy/ (LAB-001)"
    SOC2_TSC        = "CC6.1 — Logical Access Controls"
    ISO27001        = "A.9.4.1 — Information Access Restriction"
    Evidence        = "Conftest output log trong GitHub Actions run"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  },
  [PSCustomObject]@{
    ControlID       = "CTL-002"
    TechnicalControl = "Checkov CIS scan trong CI"
    Description     = "IaC files được scan theo CIS Azure Benchmark v2.0 tại mỗi PR"
    Implementation   = "checkov -d . --framework terraform --compact (LAB-002)"
    SOC2_TSC        = "CC6.8 — Prevention of Malicious Software"
    ISO27001        = "A.14.2.1 — Secure Development Policy"
    Evidence        = "Checkov JSON report artifacts trong CI"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  },
  [PSCustomObject]@{
    ControlID       = "CTL-003"
    TechnicalControl = "Azure Activity Log → Log Analytics (365 ngày)"
    Description     = "Mọi management action được log và retained ≥ 1 năm"
    Implementation   = "Diagnostic setting export sang LAW (LAB-003)"
    SOC2_TSC        = "CC7.2 — System Monitoring"
    ISO27001        = "A.12.4.1 — Event Logging"
    Evidence        = "Log Analytics AzureActivity table query export"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  },
  [PSCustomObject]@{
    ControlID       = "CTL-004"
    TechnicalControl = "Terraform Drift Detection (mỗi 6 giờ)"
    Description     = "Phát hiện và alert khi infrastructure lệch khỏi IaC state"
    Implementation   = "GitHub Actions workflow + Teams webhook (LAB-004)"
    SOC2_TSC        = "CC8.1 — Change Management"
    ISO27001        = "A.12.1.2 — Change Management"
    Evidence        = "GitHub Actions run log; Teams alert history"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  },
  [PSCustomObject]@{
    ControlID       = "CTL-005"
    TechnicalControl = "Azure Policy 'Deny' non-compliant resources"
    Description     = "Ngăn tạo resource thiếu encryption, HTTPS, tag required"
    Implementation   = "Azure Policy initiative assigned tại subscription level"
    SOC2_TSC        = "CC9.2 — Risk Monitoring"
    ISO27001        = "A.18.2.2 — Compliance with Security Policies"
    Evidence        = "Azure Policy compliance dashboard screenshot; export CSV"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  },
  [PSCustomObject]@{
    ControlID       = "CTL-006"
    TechnicalControl = "MFA enforced via Conditional Access"
    Description     = "Tất cả admin accounts yêu cầu MFA"
    Implementation   = "Entra ID Conditional Access Policy"
    SOC2_TSC        = "CC6.2 — Authentication"
    ISO27001        = "A.9.4.2 — Secure Log-on Procedures"
    Evidence        = "Entra ID Sign-in logs; Conditional Access policy export"
    Status          = "Implemented"
    ReviewDate      = "2026-06-01"
  }
)

# BƯỚC 2: Export sang CSV
$outputFile = "control-matrix-$(Get-Date -Format 'yyyyMMdd').csv"
$controls | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8
Write-Host "Control matrix saved: $outputFile ($($controls.Count) controls)"

# BƯỚC 3: Mở trong Excel để format đẹp
Start-Process $outputFile

# BƯỚC 4: Query Azure Policy compliance state và append vào matrix
Write-Host "`n=== Azure Policy Compliance Check ==="
az policy state list `
  --query "[0:10].{
    PolicyName:policyDefinitionName,
    ResourceType:resourceType,
    ComplianceState:complianceState,
    Timestamp:timestamp
  }" `
  --output table

# BƯỚC 5: Tạo summary report cho management
$summary = @"
# Compliance Control Matrix — Summary Report
**Date:** $(Get-Date -Format 'yyyy-MM-dd')
**Organization:** HoaTranLab
**Frameworks:** SOC 2 Type II, ISO 27001:2022

## Status Overview
- Total Controls: $($controls.Count)
- Implemented: $($controls | Where-Object { $_.Status -eq 'Implemented' } | Measure-Object | Select-Object -ExpandProperty Count)
- Pending: $($controls | Where-Object { $_.Status -eq 'Pending' } | Measure-Object | Select-Object -ExpandProperty Count)

## Controls by Framework
### SOC 2 TSC Coverage
$($controls | Select-Object -ExpandProperty SOC2_TSC | Sort-Object -Unique | ForEach-Object { "- $_" } | Out-String)
### ISO 27001 Annex A Coverage
$($controls | Select-Object -ExpandProperty ISO27001 | Sort-Object -Unique | ForEach-Object { "- $_" } | Out-String)

## Evidence Collection
All evidence artifacts are stored in:
- GitHub Actions run logs (CI/CD evidence)
- Azure Log Analytics workspace (audit logs)
- Azure Policy compliance dashboard (posture evidence)
"@

$summary | Out-File -FilePath "compliance-summary-$(Get-Date -Format 'yyyyMMdd').md"
Write-Host "Summary report saved."

✅ Kết quả mong đợi: File control-matrix-YYYYMMDD.csv có 6 dòng, mỗi dòng map đầy đủ ControlID → Technical Control → SOC 2 TSC → ISO 27001 → Evidence → Status. File compliance-summary-YYYYMMDD.md liệt kê coverage tổng quan. Khi mở CSV bằng Excel, có thể filter theo Status/Framework để báo cáo nhanh với management hoặc auditor.

🧹 Cleanup: Remove-Item control-matrix-*.csv, compliance-summary-*.md sau khi lưu vào hệ thống quản lý document chính thức. Không commit file có thông tin subscription ID vào git public.

3. Tình huống doanh nghiệp thực tế

Bối cảnh — Công ty SaaS chuẩn bị SOC 2 Type II audit

Một công ty SaaS B2B (100 nhân viên) lần đầu tiên trải qua SOC 2 Type II audit sau 12 tháng. Auditor yêu cầu evidence: (1) mọi thay đổi infrastructure được authorized và logged, (2) non-compliant resource bị ngăn deploy, (3) thay đổi ngoài pipeline được phát hiện, (4) access control có MFA và least privilege. Đội DevOps có 2 tuần để chuẩn bị.

Cách triển khai trong 2 tuần

  • Tuần 1 — Technical: Enable Azure Activity Log → Log Analytics (retention 1 năm); assign Azure Policy initiative "CIS Azure v2.0" với effect Audit; thêm Checkov step vào tất cả Terraform CI pipelines; deploy drift detection workflow.
  • Tuần 2 — Evidence packaging: Export 12 tháng Activity Log từ Log Analytics thành CSV (KQL query); chụp màn hình Defender for Cloud Secure Score + Policy compliance dashboard; export GitHub Actions run history cho thấy mọi deploy đều qua CI; tạo control matrix CSV map sang SOC 2 TSC.
  • Kết quả audit: Auditor xác nhận CC6.1, CC7.2, CC8.1, CC9.2 đều có evidence cụ thể, tự động, liên tục — không phải chụp màn hình thủ công tại thời điểm audit. SOC 2 Type II opinion: Unqualified (Pass).
  • Điểm then chốt: Toàn bộ evidence là continuous evidence — được thu thập tự động mỗi ngày, không phải "evidence theo yêu cầu". Đây là sự khác biệt giữa Type I (điểm thời gian) và Type II (khoảng thời gian ≥ 6 tháng).

📚 Nguồn tham khảo

Module 33: ChatOps & AIOps Module 35: Certification Roadmap & Portfolio
Zalo