diff --git a/bicep/ccw.bicep b/bicep/ccw.bicep index 9f8fb4e4..c04645a8 100644 --- a/bicep/ccw.bicep +++ b/bicep/ccw.bicep @@ -44,6 +44,72 @@ var anfDefaultMountOptions = 'rw,hard,rsize=262144,wsize=262144,vers=3,tcp,_netd func getTags(resource_type string, tags types.resource_tags_t) types.tags_t => tags[?resource_type] ?? {} +// Managed identity for SKU validation +resource skuValidationIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (!infrastructureOnly) { + name: 'ccw-sku-validation-identity' + location: location + tags: getTags('Managed Identity', tags) +} + +// Role assignment module scoped to subscription for the managed identity to read compute resources +module skuValidationRoleAssignment 'skuValidationRoleAssignment.bicep' = if (!infrastructureOnly) { + name: 'sku-validation-role-assignment' + scope: subscription() + params: { + principalId: skuValidationIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +// Deployment script to validate VM SKUs +resource skuValidationScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = if (!infrastructureOnly) { + name: 'ccw-validate-vm-skus' + location: location + kind: 'AzureCLI' + tags: getTags('Deployment Script', tags) + properties: { + azCliVersion: '2.30.0' + retentionInterval: 'PT1H' + timeout: 'PT30M' + scriptContent: loadTextContent('./validate-skus.sh') + environmentVariables: [ + { + name: 'LOCATION' + value: location + } + { + name: 'SCHEDULER_SKU' + value: schedulerNode.sku + } + { + name: 'LOGIN_SKU' + value: loginNodes.sku + } + { + name: 'HTC_SKU' + value: htc.sku + } + { + name: 'HPC_SKU' + value: hpc.sku + } + { + name: 'GPU_SKU' + value: gpu.sku + } + ] + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${skuValidationIdentity.id}': {} + } + } + dependsOn: [ + skuValidationRoleAssignment + ] +} + var useEnteredKey = adminSshPublicKey != '' module ccwPublicKey './publicKey.bicep' = if (!useEnteredKey && !infrastructureOnly) { name: 'ccwPublicKey' diff --git a/bicep/skuValidationRoleAssignment.bicep b/bicep/skuValidationRoleAssignment.bicep new file mode 100644 index 00000000..8bb4393f --- /dev/null +++ b/bicep/skuValidationRoleAssignment.bicep @@ -0,0 +1,16 @@ +targetScope = 'subscription' + +param principalId string +param principalType string + +// Role assignment for the managed identity to read compute resources +resource skuValidationRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, principalId, 'Reader') + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') // Reader role + principalId: principalId + principalType: principalType + } +} + +output roleAssignmentId string = skuValidationRoleAssignment.id diff --git a/bicep/validate-skus.sh b/bicep/validate-skus.sh new file mode 100644 index 00000000..0591e15e --- /dev/null +++ b/bicep/validate-skus.sh @@ -0,0 +1,113 @@ +#!/bin/bash +set -e +echo "Validating VM SKUs in region: ${LOCATION}" +echo "Environment variables set:" +echo "LOCATION: ${LOCATION}" +echo "SCHEDULER_SKU: ${SCHEDULER_SKU}" +echo "LOGIN_SKU: ${LOGIN_SKU}" +echo "HTC_SKU: ${HTC_SKU}" +echo "HPC_SKU: ${HPC_SKU}" +echo "GPU_SKU: ${GPU_SKU}" + +# Build dynamic query filter based on SKUs that need validation +query_filter="" +filter_conditions="" + +# Add each SKU to the filter if it's defined +if [[ -n "$HTC_SKU" ]]; then + if [[ -n "$filter_conditions" ]]; then + filter_conditions="$filter_conditions || name=='$HTC_SKU'" + else + filter_conditions="name=='$HTC_SKU'" + fi +fi + +if [[ -n "$HPC_SKU" ]]; then + if [[ -n "$filter_conditions" ]]; then + filter_conditions="$filter_conditions || name=='$HPC_SKU'" + else + filter_conditions="name=='$HPC_SKU'" + fi +fi + +if [[ -n "$GPU_SKU" ]]; then + if [[ -n "$filter_conditions" ]]; then + filter_conditions="$filter_conditions || name=='$GPU_SKU'" + else + filter_conditions="name=='$GPU_SKU'" + fi +fi + +if [[ -n "$SCHEDULER_SKU" ]]; then + if [[ -n "$filter_conditions" ]]; then + filter_conditions="$filter_conditions || name=='$SCHEDULER_SKU'" + else + filter_conditions="name=='$SCHEDULER_SKU'" + fi +fi + +if [[ -n "$LOGIN_SKU" ]]; then + if [[ -n "$filter_conditions" ]]; then + filter_conditions="$filter_conditions || name=='$LOGIN_SKU'" + else + filter_conditions="name=='$LOGIN_SKU'" + fi +fi + +if [[ -z "$filter_conditions" ]]; then + echo "[DEBUG] No specific SKUs defined yet; loading all SKUs." >&2 + query_filter="value[?locationInfo!=null]" +else + query_filter="value[?$filter_conditions]" + echo "[DEBUG] Loading SKUs with filter: $filter_conditions" >&2 +fi + +# Get subscription ID +subscription_id=$(az account show --query id -o tsv) +if [[ -z "$subscription_id" ]]; then + echo "[ERROR] Could not get subscription ID" >&2 + exit 1 +fi + +# Query Azure REST API for SKUs +echo "[DEBUG] Calling Azure REST API for SKUs..." >&2 +api_url="/subscriptions/$subscription_id/providers/Microsoft.Compute/skus?api-version=2021-07-01&\$filter=location eq '$LOCATION'" + +if ! raw=$(az rest --method get --url "$api_url" --query "$query_filter" -o json 2>&1); then + echo "[ERROR] az rest call to list SKUs failed: $raw" >&2 + exit 1 +fi + +# Parse results and validate each required SKU +if ! skus_found=$(echo "$raw" | jq -r '.[].name' 2>/dev/null | sort | uniq); then + echo "[ERROR] Failed to parse SKU results with jq" >&2 + echo "[DEBUG] Raw response: $raw" >&2 + exit 1 +fi + +echo "[DEBUG] Available SKUs found:" >&2 +echo "$skus_found" | sed 's/^/ /' >&2 + +# Validate each required SKU +validate_sku() { + local sku_name="$1" + local sku_type="$2" + echo "Validating $sku_type SKU: $sku_name" + + if echo "$skus_found" | grep -q "^$sku_name$"; then + echo "✓ $sku_type SKU '$sku_name' is available" + return 0 + else + echo "ERROR: VM SKU '$sku_name' for $sku_type nodes is not available in region '${LOCATION}'" >&2 + return 1 + fi +} + +# Validate all SKUs +validate_sku "${SCHEDULER_SKU}" "scheduler" || exit 1 +validate_sku "${LOGIN_SKU}" "login" || exit 1 +validate_sku "${HTC_SKU}" "HTC" || exit 1 +validate_sku "${HPC_SKU}" "HPC" || exit 1 +validate_sku "${GPU_SKU}" "GPU" || exit 1 + +echo "All VM SKUs validated successfully" \ No newline at end of file