Bash용 Azure CLI 스크립트 만들기

완료됨

Azure Portal에서 완료할 수 있는 거의 모든 작업은 Azure CLI 참조 명령을 사용하여 완료할 수 있습니다. Azure Portal을 사용하여 Azure에 대해 알아보는 것이 좋습니다. 그러나 Azure CLI 또는 Azure PowerShell 을 사용하여 대규모로 Azure 리소스를 관리하는 것이 좋습니다.

전 세계 기업의 Azure 를 관리하는 시나리오를 고려합니다. 새 리소스 그룹, Azure Logic Apps, 스토리지 계정, Azure Data Factory 파이프라인Azure SQL 데이터베이스에 대해 매일 여러 요청을 받습니다. 모든 팀은 개발, 스테이징 및 프로덕션 환경에서 작업합니다. 모든 요청에 대해 회사의 명명 표준 및 보안 정책을 따르는 세 가지 유사한 Azure 리소스 를 만들어야 합니다. Azure CLI 스크립팅을 사용해야 합니다.

Azure CLI 스크립트 이해

Azure CLI 스크립트를 사용하면 반복적인 작업 및 인프라 배포를 대규모로 자동화할 수 있습니다. 스크립트는 일관성을 제공하고, 사용자 오류를 줄이며, IaC(Infrastructure as Code) 사례를 사용하도록 설정합니다. 개발, 스테이징 또는 프로덕션 환경을 관리하는 경우 Azure CLI 스크립팅은 리소스 프로비저닝 및 관리를 간소화합니다.

스크립트 구조 기본 사항

잘 구성된 Azure CLI 스크립트는 일반적으로 다음을 포함합니다.

  • Shebang (#!/bin/bash 또는 #!/usr/bin/env bash): 스크립트를 실행하기 위한 인터프리터를 지정합니다.
  • 변수: 리소스 이름, 위치 및 SKU 구성과 같은 재사용 가능한 값을 저장합니다.
  • 코멘트: 유지 관리를 위한 문서 스크립트 논리 및 매개 변수입니다.
  • 오류 처리: 오류를 정상적으로 처리하도록 검사를 구현합니다.
  • 루프 및 조건부: 여러 리소스를 만들거나 조건에 따라 논리를 적용합니다.
  • 출력 서식 지정: 유효성 검사를 위해 결과를 읽을 수 있는 형식으로 표시합니다.

리소스 배포 스크립트 만들기

일관되고 반복 가능한 방식으로 여러 Azure Storage 계정을 만드는 실용적인 Bash 스크립트를 살펴보겠습니다.

#!/bin/bash

# Script: Create multiple Azure storage accounts
# Description: Automates storage account creation with consistent naming and configuration

# Define deployment parameters
resourceGroupName="rg-storage-prod-eastus"
saCount=3
saLocation="eastus"
saNamePrefix="stprod"
saSku="Standard_GRS"
saKind="StorageV2"

# Validate Azure CLI is installed and authenticated
if ! command -v az &> /dev/null; then
    echo "Error: Azure CLI is not installed. Please install Azure CLI first."
    exit 1
fi

if ! az account show &> /dev/null; then
    echo "Error: Not authenticated to Azure. Please run 'az login' first."
    exit 1
fi

echo "Starting storage account deployment..."
echo "Resource Group: $resourceGroupName"
echo "Location: $saLocation"
echo "Count: $saCount"
echo ""

# Create resource group if it doesn't exist
if ! az group show --name $resourceGroupName &> /dev/null; then
    echo "Creating resource group $resourceGroupName..."
    az group create --name $resourceGroupName --location $saLocation
else
    echo "Resource group $resourceGroupName already exists."
fi

# Loop to create multiple storage accounts
for i in $(seq 1 $saCount)
do
    # Generate unique identifier using timestamp and random number
    timestamp=$(date +%s)
    let "randomIdentifier=$RANDOM"
    saName="${saNamePrefix}${timestamp}${randomIdentifier}"

    # Ensure name is lowercase and within 24 character limit
    saName=$(echo $saName | tr '[:upper:]' '[:lower:]' | cut -c1-24)

    echo "Creating storage account $saName..."

    # Create storage account with error handling
    if az storage account create \
        --name $saName \
        --resource-group $resourceGroupName \
        --location $saLocation \
        --sku $saSku \
        --kind $saKind \
        --tags Environment=Production ManagedBy=AzureCLI \
        --output none; then
        echo "✓ Successfully created storage account $saName"
    else
        echo "✗ Failed to create storage account $saName"
    fi
done

echo ""
echo "Deployment complete. Verifying results..."
echo ""

# Verify results with formatted output
az storage account list \
    --resource-group $resourceGroupName \
    --query "[].{Name:name, Location:location, SKU:sku.name, Status:statusOfPrimary}" \
    --output table

스크립트 분석

주요 구성 요소를 살펴보겠습니다.

인증 유효성 검사:

if ! az account show &> /dev/null; then
    echo "Error: Not authenticated to Azure. Please run 'az login' first."
    exit 1
fi

리소스 작업을 실행하기 전에 사용자가 인증되어 스크립트 오류가 발생하지 않도록 합니다.

존재 확인을 사용하여 리소스 그룹 만들기:

if ! az group show --name $resourceGroupName &> /dev/null; then
    az group create --name $resourceGroupName --location $saLocation
fi

리소스 그룹이 아직 없는 경우에만 리소스 그룹을 만들어 스크립트가 항등성을 보장하도록 합니다.

고유 이름 생성:

timestamp=$(date +%s)
let "randomIdentifier=$RANDOM"
saName="${saNamePrefix}${timestamp}${randomIdentifier}"
saName=$(echo $saName | tr '[:upper:]' '[:lower:]' | cut -c1-24)

타임스탬프와 난수를 결합하여 고유한 스토리지 계정 이름을 생성하고, 소문자로 변환하고, Azure 명명 요구 사항에 따라 24자로 제한합니다.

오류 처리:

if az storage account create ...; then
    echo "✓ Successfully created storage account $saName"
else
    echo "✗ Failed to create storage account $saName"
fi

각 스토리지 계정 만들기의 유효성을 검사하고 명확한 성공/실패 피드백을 제공합니다.

매개 변수가 있는 스크립트 만들기

프로덕션 스크립트는 환경 전반에서 유연성을 위해 매개 변수를 허용해야 합니다.

#!/bin/bash

# Script: create-storage-accounts.sh
# Usage: ./create-storage-accounts.sh <resource-group> <location> <count> <environment>

# Accept command-line parameters
resourceGroupName=$1
saLocation=$2
saCount=$3
environment=$4

# Validate parameters
if [ -z "$resourceGroupName" ] || [ -z "$saLocation" ] || [ -z "$saCount" ] || [ -z "$environment" ]; then
    echo "Usage: $0 <resource-group> <location> <count> <environment>"
    echo "Example: $0 rg-storage-dev eastus 3 Development"
    exit 1
fi

# Validate count is a number
if ! [[ "$saCount" =~ ^[0-9]+$ ]]; then
    echo "Error: Count must be a number"
    exit 1
fi

echo "Parameters received:"
echo "  Resource Group: $resourceGroupName"
echo "  Location: $saLocation"
echo "  Count: $saCount"
echo "  Environment: $environment"
echo ""

# Set environment-specific configurations
case $environment in
    Development)
        saSku="Standard_LRS"
        saKind="StorageV2"
        ;;
    Staging)
        saSku="Standard_GRS"
        saKind="StorageV2"
        ;;
    Production)
        saSku="Standard_RAGRS"
        saKind="StorageV2"
        ;;
    *)
        echo "Error: Environment must be Development, Staging, or Production"
        exit 1
        ;;
esac

echo "Using SKU: $saSku for $environment environment"
echo ""

# Continue with resource creation...

매개 변수가 있는 스크립트 실행

# Make script executable
chmod +x create-storage-accounts.sh

# Execute with parameters
./create-storage-accounts.sh rg-storage-dev eastus 3 Development

구성 기반 스크립트 만들기

복잡한 배포의 경우 외부 구성 파일을 사용합니다.

config.json:

{
  "resourceGroup": {
    "name": "rg-app-prod-eastus",
    "location": "eastus"
  },
  "storageAccounts": [
    {
      "namePrefix": "stappdata",
      "sku": "Standard_RAGRS",
      "kind": "StorageV2",
      "count": 2
    },
    {
      "namePrefix": "stappbackup",
      "sku": "Standard_GRS",
      "kind": "StorageV2",
      "count": 1
    }
  ],
  "tags": {
    "Environment": "Production",
    "CostCenter": "Engineering",
    "ManagedBy": "AzureCLI"
  }
}

deploy-from-config.sh:

#!/bin/bash

# Load configuration file
configFile="config.json"

if [ ! -f "$configFile" ]; then
    echo "Error: Configuration file $configFile not found"
    exit 1
fi

# Parse configuration using jq (JSON processor)
resourceGroupName=$(jq -r '.resourceGroup.name' $configFile)
resourceGroupLocation=$(jq -r '.resourceGroup.location' $configFile)
tagsJson=$(jq -r '.tags | to_entries | map("\(.key)=\(.value)") | join(" ")' $configFile)

echo "Deploying resources from configuration file..."
echo "Resource Group: $resourceGroupName"
echo "Location: $resourceGroupLocation"
echo ""

# Create resource group
az group create --name $resourceGroupName --location $resourceGroupLocation

# Read storage account configurations
storageConfigCount=$(jq '.storageAccounts | length' $configFile)

for i in $(seq 0 $(($storageConfigCount - 1)))
do
    namePrefix=$(jq -r ".storageAccounts[$i].namePrefix" $configFile)
    sku=$(jq -r ".storageAccounts[$i].sku" $configFile)
    kind=$(jq -r ".storageAccounts[$i].kind" $configFile)
    count=$(jq -r ".storageAccounts[$i].count" $configFile)

    echo "Creating $count storage account(s) with prefix: $namePrefix"

    for j in $(seq 1 $count)
    do
        timestamp=$(date +%s)
        saName="${namePrefix}${timestamp}${RANDOM}"
        saName=$(echo $saName | tr '[:upper:]' '[:lower:]' | cut -c1-24)

        az storage account create \
            --name $saName \
            --resource-group $resourceGroupName \
            --location $resourceGroupLocation \
            --sku $sku \
            --kind $kind \
            --tags $tagsJson \
            --output none

        echo "  ✓ Created: $saName"
    done
done

echo ""
echo "Deployment complete."

구성 기반 이점

  • 우려 사항 분리: 배포 논리와 별도로 구성합니다.
  • 환경 관리: 개발, 스테이징, 프로덕션에 대해 다른 구성 파일을 사용하는 동일한 스크립트입니다.
  • 버전 제어: 코드와 함께 구성 변경 내용을 추적합니다.
  • 유효성 검사: 배포 전에 구성 구조의 유효성을 검사합니다.

스크립트를 사용하여 Azure 리소스 삭제

스크립트를 만들고 테스트할 때 불필요한 비용을 방지하기 위해 테스트 리소스를 삭제해야 합니다. Azure CLI 스크립팅을 사용하면 안전하고 일관된 리소스 정리가 가능합니다.

만든 날짜별로 스토리지 계정 삭제

특정 날짜 및 시간 이후에 만든 모든 스토리지 계정을 삭제합니다.

#!/bin/bash

# Define cleanup parameters
cutoffDate="2025-10-08T00:00:00.000000+00:00"
resourceGroup="rg-storage-dev-eastus"

echo "Finding storage accounts created on or after $cutoffDate..."

# Get list of storage accounts matching criteria
saList=$(az storage account list \
    --resource-group $resourceGroup \
    --query "[?creationTime >='$cutoffDate'].{Name:name, Created:creationTime}" \
    --output table)

echo "$saList"
echo ""

# Confirm deletion
read -p "Delete these storage accounts? (yes/no): " confirm

if [ "$confirm" == "yes" ]; then
    for saId in $(az storage account list \
        --resource-group $resourceGroup \
        --query "[?creationTime >='$cutoffDate'].id" \
        --output tsv); do
        echo "Deleting storage account: $saId"
        az storage account delete --ids $saId --yes
    done
    echo "Cleanup complete."
else
    echo "Deletion cancelled."
fi

로깅을 사용하여 리소스 그룹 삭제

포괄적인 로깅을 사용하여 명명 패턴과 일치하는 리소스 그룹을 삭제합니다.

#!/bin/bash

# Define cleanup parameters
namePattern="rg-dev-*"
logFileLocation="cleanup-$(date +%Y%m%d-%H%M%S).log"

echo "Resource Group Cleanup" > $logFileLocation
echo "Started: $(date)" >> $logFileLocation
echo "Pattern: $namePattern" >> $logFileLocation
echo "----------------------------------------" >> $logFileLocation

# Find matching resource groups
echo "Finding resource groups matching pattern: $namePattern"
matchingGroups=$(az group list \
    --query "[?starts_with(name, 'rg-dev-')].name" \
    --output tsv)

if [ -z "$matchingGroups" ]; then
    echo "No resource groups found matching pattern: $namePattern"
    echo "No resource groups found" >> $logFileLocation
    exit 0
fi

# Display matches
echo "Found resource groups:"
echo "$matchingGroups"
echo ""

# Log matches
echo "Resource groups found:" >> $logFileLocation
echo "$matchingGroups" >> $logFileLocation
echo "" >> $logFileLocation

# Confirm deletion
read -p "Delete these resource groups? (yes/no): " confirm

if [ "$confirm" == "yes" ]; then
    echo "Starting deletion..." >> $logFileLocation

    for rgName in $matchingGroups
    do
        echo "Deleting resource group: $rgName"
        echo "$(date): Deleting $rgName" >> $logFileLocation

        # Delete with --no-wait for background execution
        if az group delete --name $rgName --yes --no-wait; then
            echo "  ✓ Deletion initiated for $rgName"
            echo "$(date): ✓ Deletion initiated for $rgName" >> $logFileLocation
        else
            echo "  ✗ Failed to initiate deletion for $rgName"
            echo "$(date): ✗ Failed to delete $rgName" >> $logFileLocation
        fi
    done

    echo ""
    echo "Deletion operations initiated. Resources will be removed in the background."
    echo "Check deletion status with: az group list --query \"[?starts_with(name, 'rg-dev-')].name\""

    echo "" >> $logFileLocation
    echo "Completed: $(date)" >> $logFileLocation
    echo "Log saved to: $logFileLocation"

    # Display log
    echo ""
    echo "=== Cleanup Log ==="
    cat $logFileLocation
else
    echo "Deletion cancelled by user" >> $logFileLocation
    echo "Deletion cancelled."
fi

안전한 삭제 사례

삭제하기 전에 안전 검사를 구현합니다.

#!/bin/bash

# Delete resources safely with multiple confirmations
resourceGroup="rg-storage-test-eastus"

# Check if resource group exists
if ! az group show --name $resourceGroup &> /dev/null; then
    echo "Error: Resource group '$resourceGroup' not found"
    exit 1
fi

# Display resources that will be deleted
echo "Resources in resource group '$resourceGroup':"
az resource list --resource-group $resourceGroup \
    --query "[].{Name:name, Type:type, Location:location}" \
    --output table

echo ""
echo "⚠️  WARNING: This will delete ALL resources in '$resourceGroup'"
echo ""

# First confirmation
read -p "Are you sure you want to delete '$resourceGroup'? (yes/no): " confirm1

if [ "$confirm1" != "yes" ]; then
    echo "Deletion cancelled."
    exit 0
fi

# Second confirmation with exact name
read -p "Type the resource group name to confirm: " confirm2

if [ "$confirm2" != "$resourceGroup" ]; then
    echo "Resource group name does not match. Deletion cancelled."
    exit 1
fi

# Final countdown
echo "Deleting in 5 seconds. Press Ctrl+C to cancel."
sleep 5

echo "Deleting resource group '$resourceGroup'..."
az group delete --name $resourceGroup --yes

echo "Resource group deleted successfully."

경고

삭제하기 전에 항상 리소스를 확인합니다. 리소스 그룹을 삭제하면 리소스 그룹의 모든 리소스가 영구적으로 제거됩니다. 이 작업은 취소할 수 없습니다.

프로덕션 스크립트에 대한 모범 사례

프로덕션 준비 Azure CLI 스크립트에는 신중한 계획 및 구현이 필요합니다. 이 단원에서 제공하는 스크립트는 기본 개념을 보여 주지만 프로덕션 환경에는 추가 고려 사항이 필요합니다.

필수 프로덕션 스크립트 구성 요소

프로덕션 환경에 대한 강력한 Azure CLI 스크립트는 다음을 포함해야 합니다.

1. 인증 및 권한 부여:

#!/bin/bash

# Authenticate with service principal (for automation)
if [ -n "$AZURE_CLIENT_ID" ] && [ -n "$AZURE_CLIENT_SECRET" ] && [ -n "$AZURE_TENANT_ID" ]; then
    echo "Authenticating with service principal..."
    az login --service-principal \
        --username $AZURE_CLIENT_ID \
        --password $AZURE_CLIENT_SECRET \
        --tenant $AZURE_TENANT_ID
else
    echo "Error: Service principal credentials not found"
    exit 1
fi

# Set subscription
az account set --subscription $AZURE_SUBSCRIPTION_ID

# Verify authentication
if ! az account show &> /dev/null; then
    echo "Error: Authentication failed"
    exit 1
fi

2. 포괄적인 오류 처리:

#!/bin/bash

# Exit immediately if command fails
set -e

# Trap errors and perform cleanup
trap 'cleanup_on_error' ERR

cleanup_on_error() {
    echo "Error occurred on line $1"
    echo "Performing cleanup..."
    # Add cleanup logic here
    exit 1
}

# Enable error trapping with line numbers
trap 'cleanup_on_error $LINENO' ERR

3. 멱등성 검사:

#!/bin/bash

# Check if resource group exists before creation
create_resource_group() {
    local rgName=$1
    local location=$2

    if az group show --name $rgName &> /dev/null; then
        echo "Resource group $rgName already exists. Skipping creation."
    else
        echo "Creating resource group $rgName..."
        az group create --name $rgName --location $location
    fi
}

# Check if storage account exists
create_storage_account() {
    local saName=$1
    local rgName=$2

    if az storage account show --name $saName --resource-group $rgName &> /dev/null; then
        echo "Storage account $saName already exists. Skipping creation."
        return 0
    else
        echo "Creating storage account $saName..."
        az storage account create --name $saName --resource-group $rgName --location eastus
    fi
}

4. 구조적 로깅:

#!/bin/bash

# Define log file with timestamp
logFile="deployment-$(date +%Y%m%d-%H%M%S).log"
errorLog="errors-$(date +%Y%m%d-%H%M%S).log"

# Logging function
log() {
    local level=$1
    shift
    local message="$@"
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")

    echo "[$timestamp] [$level] $message" | tee -a $logFile

    if [ "$level" == "ERROR" ]; then
        echo "[$timestamp] $message" >> $errorLog
    fi
}

# Usage
log "INFO" "Starting deployment..."
log "ERROR" "Failed to create resource"
log "SUCCESS" "Deployment complete"

5. 구성 유효성 검사:

#!/bin/bash

# Validate required configuration
validate_config() {
    local errors=0

    if [ -z "$RESOURCE_GROUP" ]; then
        echo "Error: RESOURCE_GROUP not defined"
        ((errors++))
    fi

    if [ -z "$LOCATION" ]; then
        echo "Error: LOCATION not defined"
        ((errors++))
    fi

    # Validate location exists
    if ! az account list-locations --query "[?name=='$LOCATION']" | grep -q "$LOCATION"; then
        echo "Error: Invalid location: $LOCATION"
        ((errors++))
    fi

    if [ $errors -gt 0 ]; then
        echo "Configuration validation failed with $errors error(s)"
        exit 1
    fi

    echo "Configuration validation passed"
}

6. 태그 지정 전략:

#!/bin/bash

# Define standard tags
environment=$1  # Development, Staging, Production
costCenter=$2
owner=$3

# Create tags string
tags="Environment=$environment CostCenter=$costCenter Owner=$owner \
      CreatedBy=AzureCLI CreatedDate=$(date +%Y-%m-%d) \
      ManagedBy=Infrastructure-Team Project=WebApp"

# Apply tags to resources
az group create \
    --name $resourceGroup \
    --location $location \
    --tags $tags

az storage account create \
    --name $saName \
    --resource-group $resourceGroup \
    --location $location \
    --tags $tags

7. 롤백 기능:

#!/bin/bash

# Track created resources for rollback
createdResources=()

create_with_tracking() {
    local resourceType=$1
    local resourceName=$2

    # Attempt resource creation
    if create_resource "$resourceType" "$resourceName"; then
        createdResources+=("$resourceType:$resourceName")
        echo "✓ Created: $resourceType - $resourceName"
        return 0
    else
        echo "✗ Failed: $resourceType - $resourceName"
        return 1
    fi
}

rollback() {
    echo "Rolling back created resources..."

    for resource in "${createdResources[@]}"; do
        IFS=':' read -r type name <<< "$resource"
        echo "Deleting $type: $name"
        delete_resource "$type" "$name"
    done

    echo "Rollback complete"
}

# Use trap to rollback on error
trap rollback ERR

스크립트 조직 구조

스크립트를 논리 모듈로 구성합니다.

azure-infrastructure/
├── config/
│   ├── dev.json
│   ├── staging.json
│   └── prod.json
├── scripts/
│   ├── deploy-infrastructure.sh
│   ├── deploy-storage.sh
│   └── cleanup.sh
├── lib/
│   ├── common-functions.sh
│   ├── logging.sh
│   └── validation.sh
├── logs/
└── README.md

재사용 가능한 함수 라이브러리

common-functions.sh:

#!/bin/bash

# Load this file in other scripts: source ./lib/common-functions.sh

# Check if Azure CLI is installed
check_azure_cli() {
    if ! command -v az &> /dev/null; then
        echo "Error: Azure CLI not installed"
        exit 1
    fi
}

# Wait for resource to be ready
wait_for_resource() {
    local resourceId=$1
    local maxAttempts=30
    local attempt=1

    echo "Waiting for resource to be ready..."

    while [ $attempt -le $maxAttempts ]; do
        if az resource show --ids $resourceId &> /dev/null; then
            echo "Resource is ready"
            return 0
        fi

        echo "Attempt $attempt/$maxAttempts - waiting..."
        sleep 10
        ((attempt++))
    done

    echo "Timeout waiting for resource"
    return 1
}

# Generate unique name
generate_unique_name() {
    local prefix=$1
    local timestamp=$(date +%s)
    local random=$RANDOM
    echo "${prefix}${timestamp}${random}" | tr '[:upper:]' '[:lower:]' | cut -c1-24
}

안전하게 스크립트 테스트

격리된 환경에서 스크립트 테스트:

#!/bin/bash

# Test mode flag
TEST_MODE=${TEST_MODE:-false}

execute_command() {
    local command=$1

    if [ "$TEST_MODE" == "true" ]; then
        echo "[TEST MODE] Would execute: $command"
    else
        echo "Executing: $command"
        eval $command
    fi
}

# Usage
execute_command "az group create --name rg-test --location eastus"

테스트 모드에서 실행:

TEST_MODE=true ./deploy-infrastructure.sh

추가 리소스

이 단원의 스크립트는 변수, 루프, 조건부 및 오류 처리를 Azure CLI 명령과 결합할 때 Azure CLI 기능을 보여 줍니다. 이러한 패턴은 몇 가지 리소스 관리에서 복잡한 엔터프라이즈 인프라 배포 오케스트레이션에 이르기까지 확장됩니다.