# Script Refactoring Guide This guide explains how to refactor existing scripts to use the new common library structure. ## Quick Start ### Before (Old Pattern) ```bash #!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Load subscription ID if [ -f "$PROJECT_ROOT/.env" ]; then export $(grep -v '^#' "$PROJECT_ROOT/.env" | grep AZURE_SUBSCRIPTION_ID | xargs) fi SUBSCRIPTION_ID="${AZURE_SUBSCRIPTION_ID:-fc08d829-4f14-413d-ab27-ce024425db0b}" # Check Azure CLI if ! command -v az &> /dev/null; then echo -e "${RED}Error: Azure CLI not found${NC}" exit 1 fi # Check Azure login az account show &> /dev/null || { echo -e "${RED}Error: Not logged in to Azure${NC}" exit 1 } az account set --subscription "$SUBSCRIPTION_ID" &> /dev/null || true echo -e "${GREEN}Starting script...${NC}" ``` ### After (New Pattern) ```bash #!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" # Initialize Azure SUBSCRIPTION_ID="$(get_subscription_id)" ensure_azure_cli || exit 1 set_subscription "$SUBSCRIPTION_ID" || true log_info "Starting script..." ``` ## Migration Checklist ### Step 1: Add Library Sourcing Replace: ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" ``` With: ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" ``` **Note**: `PROJECT_ROOT` is automatically set by the library. ### Step 2: Remove Color Definitions Remove: ```bash RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' ``` Use logging functions instead: ```bash log_info "Message" log_warn "Warning" log_error "Error" log_success "Success" ``` ### Step 3: Replace Echo Statements | Old | New | |-----|-----| | `echo -e "${GREEN}Success${NC}"` | `log_success "Success"` | | `echo -e "${RED}Error${NC}"` | `log_error "Error"` | | `echo -e "${YELLOW}Warning${NC}"` | `log_warn "Warning"` | | `echo "Info"` | `log_info "Info"` | ### Step 4: Replace Azure CLI Checks Remove: ```bash if ! command -v az &> /dev/null; then echo -e "${RED}Error: Azure CLI not found${NC}" exit 1 fi az account show &> /dev/null || { echo -e "${RED}Error: Not logged in${NC}" exit 1 } ``` Replace with: ```bash ensure_azure_cli || exit 1 ``` ### Step 5: Replace Subscription Loading Remove: ```bash if [ -f "$PROJECT_ROOT/.env" ]; then export $(grep -v '^#' "$PROJECT_ROOT/.env" | grep AZURE_SUBSCRIPTION_ID | xargs) fi SUBSCRIPTION_ID="${AZURE_SUBSCRIPTION_ID:-fc08d829-4f14-413d-ab27-ce024425db0b}" az account set --subscription "$SUBSCRIPTION_ID" &> /dev/null || true ``` Replace with: ```bash SUBSCRIPTION_ID="$(get_subscription_id)" set_subscription "$SUBSCRIPTION_ID" || true ``` ### Step 6: Replace Region Code Mappings Remove large `declare -A REGION_CODES` blocks and replace with: ```bash # Get all regions get_all_regions # Get specific region code CODE=$(get_region_code "westeurope") # Returns "wst" # Get region name from code REGION=$(get_region_name "wst") # Returns "westeurope" ``` ## Common Patterns ### Pattern 1: Script with Azure Operations ```bash #!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" ensure_azure_cli || exit 1 log_section "Script Title" # Your script logic here ``` ### Pattern 2: Script with Region Iteration ```bash #!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" ensure_azure_cli || exit 1 log_section "Processing Regions" while IFS=':' read -r region_name region_code; do log_info "Processing $region_name ($region_code)..." # Your logic here done < <(get_all_regions) ``` ### Pattern 3: Script with Error Handling ```bash #!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" ensure_azure_cli || exit 1 ERRORS=() # Process items for item in "${ITEMS[@]}"; do if ! process_item "$item"; then ERRORS+=("$item") log_error "Failed to process $item" else log_success "Processed $item" fi done # Report errors if [ ${#ERRORS[@]} -gt 0 ]; then log_warn "Failed to process ${#ERRORS[@]} items" exit 1 fi log_success "All items processed successfully" ``` ## Available Library Functions ### Logging (`lib/common/logging.sh`) - `log_debug "message"` - Debug level log - `log_info "message"` - Info level log - `log_warn "message"` - Warning log - `log_error "message"` - Error log - `log_success "message"` - Success message - `log_failure "message"` - Failure message - `log_section "title"` - Section header - `log_subsection "title"` - Subsection header ### Colors (`lib/common/colors.sh`) - `color_red "text"` - Red text - `color_green "text"` - Green text - `color_yellow "text"` - Yellow text - `color_blue "text"` - Blue text - `color_cyan "text"` - Cyan text ### Azure CLI (`lib/azure/cli.sh`) - `check_azure_cli` - Check if Azure CLI is installed - `check_azure_login` - Check if logged in - `ensure_azure_cli` - Ensure CLI is ready (installed + logged in) - `get_current_subscription` - Get current subscription ID - `get_current_subscription_name` - Get current subscription name ### Configuration (`lib/config/`) - `get_subscription_id` - Get subscription ID from env or default - `set_subscription "id"` - Set Azure subscription - `get_region_code "name"` - Get 3-char region code - `get_region_name "code"` - Get region name from code - `get_all_regions` - Get all regions (name:code format) - `is_valid_region_code "code"` - Validate region code ### Utilities (`lib/common/utils.sh`) - `require_command "cmd" ["hint"]` - Require command exists - `command_exists "cmd"` - Check if command exists - `confirm "prompt" ["default"]` - Confirm action - `is_dry_run` - Check if in dry-run mode - `print_header "title" ["width"]` - Print header box ## Testing Refactored Scripts 1. Test with `--dry-run` if supported 2. Verify output formatting matches original 3. Check error messages are clear 4. Ensure backward compatibility if script is called by others ## Examples See `scripts/azure/check-naming-conventions.sh.refactored` for a complete example of a refactored script. ```bash # Example migrated scripts (as references): # - scripts/deployment/fix-resource-groups-and-keyvaults.sh # - scripts/azure/list-all-resources.sh # - scripts/key-management/manage-keyvaults.sh # - scripts/key-management/azure-keyvault-setup.sh # - scripts/key-management/store-nodes-in-keyvault.sh ``` ## Testing ```bash # Syntax check only (no execution): bash -n scripts/azure/list-all-resources.sh bash -n scripts/key-management/manage-keyvaults.sh bash -n scripts/key-management/azure-keyvault-setup.sh bash -n scripts/key-management/store-nodes-in-keyvault.sh # Dry run (where supported): DRY_RUN=1 scripts/key-management/store-nodes-in-keyvault.sh ```