## Code Template

```typescript
/**
 * FedRAMP 20x Evidence Collection - Generic TypeScript/Node.js Template
 * 
 * Collects compliance evidence from Azure resources and stores in Azure Blob Storage.
 * Uses DefaultAzureCredential for authentication (supports managed identity and local dev).
 * 
 * Azure SDK References:
 * - Azure Identity: https://learn.microsoft.com/javascript/api/overview/azure/identity-readme
 * - Azure Storage: https://learn.microsoft.com/javascript/api/overview/azure/storage-blob-readme
 * - Azure Resource Manager: https://learn.microsoft.com/javascript/api/overview/azure/arm-resources-readme
 * - Microsoft Graph SDK: https://learn.microsoft.com/graph/sdks/sdks-overview
 */

import { DefaultAzureCredential } from '@azure/identity';
import { BlobServiceClient, ContainerClient } from '@azure/storage-blob';
import { ResourceManagementClient } from '@azure/arm-resources';
import { Client as GraphClient } from '@microsoft/microsoft-graph-client';
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';

// Configuration
const STORAGE_ACCOUNT = 'stafedrampevidence';
const CONTAINER_NAME = 'ksi-evidence';
const SUBSCRIPTION_ID = process.env.AZURE_SUBSCRIPTION_ID || '';

interface Evidence {
    timestamp: string;
    collection_method: string;
    [key: string]: any;
}

class EvidenceCollector {
    private credential: DefaultAzureCredential;
    private resourceClient: ResourceManagementClient;
    private graphClient: GraphClient;
    private blobServiceClient: BlobServiceClient;
    
    constructor() {
        // Initialize Azure authentication
        // This supports managed identity in Azure and Azure CLI for local development
        // Azure WAF Security: https://learn.microsoft.com/azure/well-architected/security/
        this.credential = new DefaultAzureCredential();
        
        // Initialize Azure Resource Manager client for querying Azure resources
        this.resourceClient = new ResourceManagementClient(
            this.credential,
            SUBSCRIPTION_ID
        );
        
        // Initialize Microsoft Graph client for identity and access data
        const authProvider = new TokenCredentialAuthenticationProvider(
            this.credential,
            { scopes: ['https://graph.microsoft.com/.default'] }
        );
        
        this.graphClient = GraphClient.initWithMiddleware({
            authProvider: authProvider
        });
        
        // Initialize Azure Blob Storage client for evidence storage
        const storageUrl = `https://${STORAGE_ACCOUNT}.blob.core.windows.net`;
        this.blobServiceClient = new BlobServiceClient(storageUrl, this.credential);
        
        console.log('Evidence collector initialized successfully');
    }
    
    /**
     * Collect compliance evidence from Azure resources.
     * This is a generic template - customize the collection logic for specific KSI requirements.
     */
    async collectEvidence(): Promise<Evidence> {
        try {
            console.log('Starting evidence collection...');
            
            const evidence: Evidence = {
                timestamp: new Date().toISOString(),
                collection_method: 'automated'
            };
            
            // Example: Query Azure resources (customize for specific KSI)
            // Azure Resource Manager API: https://learn.microsoft.com/rest/api/resources/
            console.log('Querying Azure resources...');
            const resources = [];
            for await (const resource of this.resourceClient.resources.list()) {
                resources.push(resource);
            }
            
            evidence.resource_count = resources.length;
            evidence.subscription_id = SUBSCRIPTION_ID;
            
            // Example: Query Microsoft Graph (for IAM evidence)
            // Graph API: https://learn.microsoft.com/graph/overview
            console.log('Querying Microsoft Graph API...');
            try {
                const usersResponse = await this.graphClient
                    .api('/users')
                    .select('id,displayName,userPrincipalName')
                    .get();
                
                evidence.user_count = usersResponse.value?.length || 0;
            } catch (error: any) {
                console.warn(
                    `Unable to query Graph API (may need additional permissions): ${error.message}`
                );
                evidence.graph_api_error = error.message;
            }
            
            console.log('Evidence collection completed successfully');
            return evidence;
            
        } catch (error: any) {
            console.error('Error collecting evidence:', error);
            throw new Error(`Evidence collection failed: ${error.message}`);
        }
    }
    
    /**
     * Store evidence in Azure Blob Storage with immutability and metadata.
     * FRR-ADS requirement: Machine-readable format with proper metadata tagging.
     * Azure Blob Immutability: https://learn.microsoft.com/azure/storage/blobs/immutable-storage-overview
     */
    async storeEvidence(ksiId: string, evidence: Evidence): Promise<void> {
        try {
            console.log(`Storing evidence for ${ksiId}`);
            
            // Get or create container
            const containerClient: ContainerClient = this.blobServiceClient
                .getContainerClient(CONTAINER_NAME);
            
            const containerExists = await containerClient.exists();
            if (!containerExists) {
                await containerClient.create();
                console.log(`Created evidence container: ${CONTAINER_NAME}`);
            }
            
            // Generate blob name with timestamp
            const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
            const blobName = `${ksiId}/${timestamp}-evidence.json`;
            
            // Convert evidence to JSON
            const jsonEvidence = JSON.stringify(evidence, null, 2);
            
            // Upload to blob storage
            const blobClient = containerClient.getBlockBlobClient(blobName);
            await blobClient.upload(
                jsonEvidence,
                Buffer.byteLength(jsonEvidence),
                {
                    blobHTTPHeaders: {
                        blobContentType: 'application/json'
                    },
                    metadata: {
                        ksi_id: ksiId,
                        collection_date: new Date().toISOString(),
                        format: 'json',
                        compliance_framework: 'fedramp-20x'
                    }
                }
            );
            
            console.log(`Evidence stored successfully: ${blobName}`);
            console.log(`Blob URL: ${blobClient.url}`);
            
        } catch (error: any) {
            console.error('Error storing evidence:', error);
            throw new Error(`Evidence storage failed: ${error.message}`);
        }
    }
    
    /**
     * Main execution: collect and store evidence.
     */
    async run(ksiId: string): Promise<void> {
        try {
            console.log('=== Starting FedRAMP 20x Evidence Collection ===');
            console.log(`KSI ID: ${ksiId}`);
            
            // Collect evidence
            const evidence = await this.collectEvidence();
            
            // Store evidence
            await this.storeEvidence(ksiId, evidence);
            
            console.log('=== Evidence Collection Completed Successfully ===');
            
        } catch (error: any) {
            console.error(`Evidence collection failed: ${error.message}`);
            process.exit(1);
        }
    }
}

/**
 * Main entry point for standalone execution.
 */
async function main() {
    const ksiId = process.argv[2];
    
    if (!ksiId) {
        console.error('Usage: ts-node evidence-collector.ts <KSI_ID>');
        process.exit(1);
    }
    
    if (!SUBSCRIPTION_ID) {
        console.error('Error: AZURE_SUBSCRIPTION_ID environment variable not set');
        process.exit(1);
    }
    
    const collector = new EvidenceCollector();
    await collector.run(ksiId);
}

// Run if executed directly
if (require.main === module) {
    main().catch(error => {
        console.error('Fatal error:', error);
        process.exit(1);
    });
}

export { EvidenceCollector };
```

## Package Dependencies (package.json)

```json
{
  "name": "fedramp-evidence-collector",
  "version": "1.0.0",
  "description": "FedRAMP 20x Evidence Collection Automation",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/evidence-collector.js",
    "dev": "ts-node src/evidence-collector.ts"
  },
  "dependencies": {
    "@azure/identity": "^4.0.0",
    "@azure/storage-blob": "^12.17.0",
    "@azure/arm-resources": "^5.2.0",
    "@microsoft/microsoft-graph-client": "^3.0.7",
    "@microsoft/microsoft-graph-types": "^2.40.0"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "typescript": "^5.3.0",
    "ts-node": "^10.9.0"
  }
}
```

## TypeScript Configuration (tsconfig.json)

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## Setup Instructions

### 1. Install Dependencies
```bash
npm install
# or
yarn install
```

### 2. Authentication Setup
```bash
# For local development, authenticate with Azure CLI
az login

# For production, use Managed Identity (no code changes needed)
# Azure Managed Identity: https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview
```

### 3. Configure Environment Variables
```bash
# Create .env file (or set in Azure App Service Configuration)
export AZURE_SUBSCRIPTION_ID="your-subscription-id"
export AZURE_STORAGE_ACCOUNT="stafedrampevidence"  # Optional override
```

### 4. Grant Permissions
Required Azure RBAC roles:
- **Reader** - Query Azure resources
- **Storage Blob Data Contributor** - Write evidence to blob storage
- **Directory.Read.All** (Graph API) - Query user/group data for IAM evidence

### 5. Build and Run
```bash
# Development mode
npm run dev KSI-XXX-01

# Production build
npm run build
npm start KSI-XXX-01
```

## Deployment Options

### Azure Functions (Recommended)
Deploy as an Azure Function with timer trigger for scheduled collection:
```bash
# Install Azure Functions Core Tools
npm install -g azure-functions-core-tools@4

# Create function app
func init --typescript
func new --name EvidenceCollector --template "Timer trigger"

# Deploy
func azure functionapp publish <your-function-app-name>
```

### Azure App Service
Deploy as a Node.js web app with scheduled WebJobs:
- Uses Node.js 18+ runtime
- Managed Identity enabled
- Application Insights for monitoring

### Docker Container
Run as containerized workload in Azure Container Instances or AKS:
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
CMD ["node", "dist/evidence-collector.js", "${KSI_ID}"]
```

### GitHub Actions (CI/CD)
```yaml
name: Evidence Collection
on:
  schedule:
    - cron: '0 0 * * *'  # Daily at midnight
jobs:
  collect:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
      - run: npm ci
      - run: npm run build
      - uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      - run: npm start KSI-XXX-01
```

## Framework Integration Examples

### Express.js API Endpoint
```typescript
import express from 'express';
import { EvidenceCollector } from './evidence-collector';

const app = express();

app.post('/api/collect-evidence/:ksiId', async (req, res) => {
    try {
        const collector = new EvidenceCollector();
        await collector.run(req.params.ksiId);
        res.json({ success: true, message: 'Evidence collected' });
    } catch (error) {
        res.status(500).json({ success: false, error: error.message });
    }
});
```

### NestJS Service
```typescript
import { Injectable } from '@nestjs/common';
import { EvidenceCollector } from './evidence-collector';

@Injectable()
export class EvidenceService {
    async collectEvidence(ksiId: string) {
        const collector = new EvidenceCollector();
        await collector.run(ksiId);
    }
}
```

## References

- **Azure SDK for JavaScript**: https://learn.microsoft.com/javascript/api/overview/azure/
- **Azure Identity Library**: https://learn.microsoft.com/javascript/api/overview/azure/identity-readme
- **Azure Storage Blobs**: https://learn.microsoft.com/javascript/api/overview/azure/storage-blob-readme
- **Azure Resource Manager**: https://learn.microsoft.com/javascript/api/overview/azure/arm-resources-readme
- **Microsoft Graph SDK**: https://learn.microsoft.com/graph/sdks/sdks-overview
- **Azure Well-Architected Framework**: https://learn.microsoft.com/azure/well-architected/
