Files
Reza Rezvani 2056ba251f feat(engineering-team): add azure-cloud-architect, security-pen-testing; extend terraform-patterns
azure-cloud-architect (451-line SKILL.md, 3 scripts, 3 references):
- 6-step workflow mirroring aws-solution-architect for Azure
- Bicep/ARM templates, AKS, Functions, Cosmos DB, cost optimization
- architecture_designer.py, cost_optimizer.py, bicep_generator.py

security-pen-testing (850-line SKILL.md, 3 scripts, 3 references):
- OWASP Top 10 systematic audit, offensive security testing
- XSS/SQLi/SSRF/IDOR detection, secret scanning, API security
- vulnerability_scanner.py, dependency_auditor.py, pentest_report_generator.py
- Responsible disclosure workflow included

terraform-patterns extended (487 → 740 lines):
- Multi-cloud provider configuration
- OpenTofu compatibility notes
- Infracost integration for PR cost estimation
- Import existing infrastructure patterns
- Terragrunt DRY multi-environment patterns

Updated engineering-team plugin.json (26 → 28 skills).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:32:22 +01:00

776 lines
23 KiB
Python

#!/usr/bin/env python3
"""
Azure Bicep template generator.
Generates Bicep infrastructure-as-code scaffolds for common Azure architecture patterns.
Usage:
python bicep_generator.py --arch-type web-app
python bicep_generator.py --arch-type microservices --output main.bicep
python bicep_generator.py --arch-type serverless --json
python bicep_generator.py --help
"""
import argparse
import json
import sys
from typing import Dict
# ---------------------------------------------------------------------------
# Bicep templates
# ---------------------------------------------------------------------------
def _web_app_template() -> str:
return r"""// =============================================================================
// Azure Web App Architecture — Bicep Template
// App Service + Azure SQL + Front Door + Key Vault + Application Insights
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name (lowercase, no spaces)')
@minLength(3)
@maxLength(20)
param appName string
@description('SQL admin Entra ID object ID')
param sqlAdminObjectId string
// ---------------------------------------------------------------------------
// Key Vault
// ---------------------------------------------------------------------------
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: '${environment}-${appName}-kv'
location: location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableRbacAuthorization: true
enableSoftDelete: true
softDeleteRetentionInDays: 30
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// App Service Plan + App Service
// ---------------------------------------------------------------------------
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: '${environment}-${appName}-plan'
location: location
sku: {
name: environment == 'production' ? 'P1v3' : 'B1'
tier: environment == 'production' ? 'PremiumV3' : 'Basic'
capacity: 1
}
properties: {
reserved: true // Linux
}
tags: {
environment: environment
'app-name': appName
}
}
resource appService 'Microsoft.Web/sites@2023-01-01' = {
name: '${environment}-${appName}-web'
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'NODE|20-lts'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
alwaysOn: environment == 'production'
healthCheckPath: '/health'
}
}
identity: {
type: 'SystemAssigned'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Azure SQL (Serverless)
// ---------------------------------------------------------------------------
resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = {
name: '${environment}-${appName}-sql'
location: location
properties: {
administrators: {
administratorType: 'ActiveDirectory'
azureADOnlyAuthentication: true
principalType: 'Group'
sid: sqlAdminObjectId
tenantId: subscription().tenantId
}
minimalTlsVersion: '1.2'
publicNetworkAccess: environment == 'production' ? 'Disabled' : 'Enabled'
}
tags: {
environment: environment
'app-name': appName
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-05-01-preview' = {
parent: sqlServer
name: '${appName}-db'
location: location
sku: {
name: 'GP_S_Gen5_2'
tier: 'GeneralPurpose'
}
properties: {
autoPauseDelay: environment == 'production' ? -1 : 60
minCapacity: json('0.5')
zoneRedundant: environment == 'production'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Application Insights + Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: environment == 'production' ? 90 : 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output appServiceUrl string = 'https://${appService.properties.defaultHostName}'
output keyVaultUri string = keyVault.properties.vaultUri
output appInsightsKey string = appInsights.properties.InstrumentationKey
output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName
"""
def _microservices_template() -> str:
return r"""// =============================================================================
// Azure Microservices Architecture — Bicep Template
// AKS + API Management + Cosmos DB + Service Bus + Key Vault
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
@description('AKS admin Entra ID group object ID')
param aksAdminGroupId string
// ---------------------------------------------------------------------------
// Key Vault
// ---------------------------------------------------------------------------
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: '${environment}-${appName}-kv'
location: location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableRbacAuthorization: true
enableSoftDelete: true
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// AKS Cluster
// ---------------------------------------------------------------------------
resource aksCluster 'Microsoft.ContainerService/managedClusters@2024-01-01' = {
name: '${environment}-${appName}-aks'
location: location
identity: { type: 'SystemAssigned' }
properties: {
dnsPrefix: '${environment}-${appName}'
kubernetesVersion: '1.29'
enableRBAC: true
aadProfile: {
managed: true
adminGroupObjectIDs: [aksAdminGroupId]
enableAzureRBAC: true
}
networkProfile: {
networkPlugin: 'azure'
networkPolicy: 'azure'
serviceCidr: '10.0.0.0/16'
dnsServiceIP: '10.0.0.10'
}
agentPoolProfiles: [
{
name: 'system'
count: environment == 'production' ? 3 : 1
vmSize: 'Standard_D2s_v5'
mode: 'System'
enableAutoScaling: true
minCount: 1
maxCount: 3
availabilityZones: environment == 'production' ? ['1', '2', '3'] : []
}
{
name: 'app'
count: environment == 'production' ? 3 : 1
vmSize: 'Standard_D4s_v5'
mode: 'User'
enableAutoScaling: true
minCount: 1
maxCount: 10
availabilityZones: environment == 'production' ? ['1', '2', '3'] : []
}
]
addonProfiles: {
omsagent: {
enabled: true
config: {
logAnalyticsWorkspaceResourceID: logAnalytics.id
}
}
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Container Registry
// ---------------------------------------------------------------------------
resource acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
name: '${environment}${appName}acr'
location: location
sku: { name: environment == 'production' ? 'Standard' : 'Basic' }
properties: {
adminUserEnabled: false
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Cosmos DB (Serverless for dev, Autoscale for prod)
// ---------------------------------------------------------------------------
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: '${environment}-${appName}-cosmos'
location: location
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{ locationName: location, failoverPriority: 0, isZoneRedundant: environment == 'production' }
]
capabilities: environment == 'dev' ? [{ name: 'EnableServerless' }] : []
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Service Bus
// ---------------------------------------------------------------------------
resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
name: '${environment}-${appName}-sb'
location: location
sku: { name: 'Standard', tier: 'Standard' }
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Log Analytics + Application Insights
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: environment == 'production' ? 90 : 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output aksClusterName string = aksCluster.name
output acrLoginServer string = acr.properties.loginServer
output cosmosEndpoint string = cosmosAccount.properties.documentEndpoint
output serviceBusEndpoint string = '${serviceBus.name}.servicebus.windows.net'
output keyVaultUri string = keyVault.properties.vaultUri
"""
def _serverless_template() -> str:
return r"""// =============================================================================
// Azure Serverless Architecture — Bicep Template
// Azure Functions + Event Grid + Service Bus + Cosmos DB
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
// ---------------------------------------------------------------------------
// Storage Account (required by Functions)
// ---------------------------------------------------------------------------
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: '${environment}${appName}st'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Azure Functions (Consumption Plan)
// ---------------------------------------------------------------------------
resource functionPlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: '${environment}-${appName}-func-plan'
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
}
properties: {
reserved: true // Linux
}
tags: {
environment: environment
'app-name': appName
}
}
resource functionApp 'Microsoft.Web/sites@2023-01-01' = {
name: '${environment}-${appName}-func'
location: location
kind: 'functionapp,linux'
identity: { type: 'SystemAssigned' }
properties: {
serverFarmId: functionPlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'NODE|20'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
appSettings: [
{ name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=core.windows.net;AccountKey=${storageAccount.listKeys().keys[0].value}' }
{ name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
{ name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
{ name: 'APPINSIGHTS_INSTRUMENTATIONKEY', value: appInsights.properties.InstrumentationKey }
{ name: 'COSMOS_ENDPOINT', value: cosmosAccount.properties.documentEndpoint }
{ name: 'SERVICE_BUS_CONNECTION', value: listKeys('${serviceBus.id}/AuthorizationRules/RootManageSharedAccessKey', serviceBus.apiVersion).primaryConnectionString }
]
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Cosmos DB (Serverless)
// ---------------------------------------------------------------------------
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: '${environment}-${appName}-cosmos'
location: location
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{ locationName: location, failoverPriority: 0 }
]
capabilities: [{ name: 'EnableServerless' }]
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Service Bus
// ---------------------------------------------------------------------------
resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
name: '${environment}-${appName}-sb'
location: location
sku: { name: 'Basic', tier: 'Basic' }
tags: {
environment: environment
'app-name': appName
}
}
resource orderQueue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' = {
parent: serviceBus
name: 'orders'
properties: {
maxDeliveryCount: 5
defaultMessageTimeToLive: 'P7D'
deadLetteringOnMessageExpiration: true
lockDuration: 'PT1M'
}
}
// ---------------------------------------------------------------------------
// Application Insights + Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output functionAppUrl string = 'https://${functionApp.properties.defaultHostName}'
output cosmosEndpoint string = cosmosAccount.properties.documentEndpoint
output serviceBusEndpoint string = '${serviceBus.name}.servicebus.windows.net'
output appInsightsKey string = appInsights.properties.InstrumentationKey
"""
def _data_pipeline_template() -> str:
return r"""// =============================================================================
// Azure Data Pipeline Architecture — Bicep Template
// Event Hubs + Data Lake Gen2 + Synapse Analytics + Azure Functions
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
// ---------------------------------------------------------------------------
// Data Lake Storage Gen2
// ---------------------------------------------------------------------------
resource dataLake 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: '${environment}${appName}dl'
location: location
sku: { name: environment == 'production' ? 'Standard_ZRS' : 'Standard_LRS' }
kind: 'StorageV2'
properties: {
isHnsEnabled: true // Hierarchical namespace for Data Lake Gen2
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
tags: {
environment: environment
'app-name': appName
}
}
resource rawContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${dataLake.name}/default/raw'
properties: { publicAccess: 'None' }
}
resource curatedContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${dataLake.name}/default/curated'
properties: { publicAccess: 'None' }
}
// ---------------------------------------------------------------------------
// Event Hubs
// ---------------------------------------------------------------------------
resource eventHubNamespace 'Microsoft.EventHub/namespaces@2023-01-01-preview' = {
name: '${environment}-${appName}-eh'
location: location
sku: {
name: 'Standard'
tier: 'Standard'
capacity: environment == 'production' ? 2 : 1
}
properties: {
minimumTlsVersion: '1.2'
}
tags: {
environment: environment
'app-name': appName
}
}
resource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2023-01-01-preview' = {
parent: eventHubNamespace
name: 'ingest'
properties: {
partitionCount: environment == 'production' ? 8 : 2
messageRetentionInDays: 7
}
}
resource consumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2023-01-01-preview' = {
parent: eventHub
name: 'processing'
}
// ---------------------------------------------------------------------------
// Synapse Analytics (Serverless SQL)
// ---------------------------------------------------------------------------
resource synapse 'Microsoft.Synapse/workspaces@2021-06-01' = {
name: '${environment}-${appName}-syn'
location: location
identity: { type: 'SystemAssigned' }
properties: {
defaultDataLakeStorage: {
accountUrl: 'https://${dataLake.name}.dfs.core.windows.net'
filesystem: 'curated'
}
sqlAdministratorLogin: 'sqladmin'
sqlAdministratorLoginPassword: 'REPLACE_WITH_KEYVAULT_REFERENCE'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 30
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output dataLakeEndpoint string = 'https://${dataLake.name}.dfs.core.windows.net'
output eventHubNamespace string = eventHubNamespace.name
output synapseEndpoint string = synapse.properties.connectivityEndpoints.sql
"""
TEMPLATES: Dict[str, callable] = {
"web-app": _web_app_template,
"microservices": _microservices_template,
"serverless": _serverless_template,
"data-pipeline": _data_pipeline_template,
}
TEMPLATE_DESCRIPTIONS = {
"web-app": "App Service + Azure SQL + Front Door + Key Vault + Application Insights",
"microservices": "AKS + API Management + Cosmos DB + Service Bus + Key Vault",
"serverless": "Azure Functions + Event Grid + Service Bus + Cosmos DB",
"data-pipeline": "Event Hubs + Data Lake Gen2 + Synapse Analytics + Azure Functions",
}
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="Azure Bicep Generator — generate Bicep IaC templates for common Azure architecture patterns.",
epilog="Examples:\n"
" python bicep_generator.py --arch-type web-app\n"
" python bicep_generator.py --arch-type microservices --output main.bicep\n"
" python bicep_generator.py --arch-type serverless --json",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--arch-type",
required=True,
choices=list(TEMPLATES.keys()),
help="Architecture pattern type",
)
parser.add_argument(
"--output",
type=str,
default=None,
help="Write Bicep to file instead of stdout",
)
parser.add_argument(
"--json",
action="store_true",
dest="json_output",
help="Output metadata as JSON (template content + description)",
)
args = parser.parse_args()
template_fn = TEMPLATES[args.arch_type]
bicep_content = template_fn()
if args.json_output:
result = {
"arch_type": args.arch_type,
"description": TEMPLATE_DESCRIPTIONS[args.arch_type],
"bicep_template": bicep_content,
"lines": len(bicep_content.strip().split("\n")),
}
print(json.dumps(result, indent=2))
elif args.output:
with open(args.output, "w") as f:
f.write(bicep_content)
print(f"Bicep template written to {args.output} ({len(bicep_content.strip().split(chr(10)))} lines)")
print(f"Pattern: {TEMPLATE_DESCRIPTIONS[args.arch_type]}")
print(f"\nNext steps:")
print(f" 1. az bicep build --file {args.output}")
print(f" 2. az deployment group validate --resource-group <rg> --template-file {args.output}")
print(f" 3. az deployment group create --resource-group <rg> --template-file {args.output}")
else:
print(bicep_content)
if __name__ == "__main__":
main()