Créer un script Azure CLI pour Bash

Effectué

À peu près toutes les tâches que vous pouvez effectuer dans le portail Azure, vous pouvez effectuer à l’aide des commandes de référence Azure CLI . L’utilisation du portail Azure pour en savoir plus sur Azure est un excellent point de départ. Toutefois, nous vous recommandons d’utiliser Azure CLI ou Azure PowerShell pour gérer les ressources Azure à grande échelle.

Envisagez le scénario dans lequel vous gérez Azure pour une société mondiale. Vous recevez plusieurs demandes quotidiennes pour les nouveaux groupes de ressources, Azure Logic Apps, les comptes de stockage, les pipelines Azure Data Factory et lesbases de données Azure SQL. Toutes vos équipes travaillent dans des environnements de développement, de préproduction et de production. Pour chaque demande, vous devez créer trois ressources Azure similaires qui suivent les normes d’affectation de noms et les stratégies de sécurité de votre entreprise. Il est temps d’utiliser des scripts Azure CLI !

Présentation des scripts Azure CLI

Les scripts Azure CLI permettent l’automatisation des tâches répétitives et du déploiement d’infrastructure à grande échelle. Les scripts fournissent une cohérence, réduisent les erreurs humaines et activent les pratiques d’infrastructure en tant que code (IaC). Que vous gériez des environnements de développement, de préproduction ou de production, le script Azure CLI simplifie l’approvisionnement et la gestion des ressources.

Notions de base de la structure de script

Un script Azure CLI bien structuré inclut généralement les éléments suivants :

  • Shebang (#!/bin/bash ou #!/usr/bin/env bash) : spécifie l’interpréteur pour l’exécution du script.
  • Variables: Stockez des valeurs réutilisables telles que les noms de ressources, les emplacements et les configurations de référence SKU.
  • Commentaires: Documentez la logique de script et les paramètres pour la maintenance.
  • Gestion des erreurs : Implémentez des vérifications pour gérer correctement les défaillances.
  • Boucles et conditions : Créez plusieurs ressources ou appliquez une logique basée sur des conditions.
  • Mise en forme de sortie : Afficher les résultats dans des formats lisibles pour la validation.

Créer un script de déploiement de ressources

Examinons un script Bash pratique qui crée plusieurs comptes de stockage Azure de manière cohérente et reproductible.

#!/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

Répartition des scripts

Examinons les composants clés :

Validation de l’authentification :

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

Garantit que l’utilisateur est authentifié avant d’exécuter des opérations de ressources, ce qui empêche les échecs de script.

Création du groupe de ressources avec vérification de l'existence :

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

Crée le groupe de ressources uniquement s’il n’existe pas déjà, ce qui rend le script idempotent.

Génération de nom unique :

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

Combine l’horodatage et le nombre aléatoire pour générer des noms de compte de stockage uniques, les convertir en minuscules et les limiter à 24 caractères conformément aux règles de nommage Azure.

Gestion des erreurs :

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

Valide chaque création de compte de stockage et fournit des commentaires clairs sur la réussite/l’échec.

Créer un script paramétrable

Les scripts de production doivent accepter des paramètres pour la flexibilité entre les environnements :

#!/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...

Exécuter le script paramétrable

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

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

Créer un script piloté par la configuration

Pour les déploiements complexes, utilisez des fichiers de configuration externes :

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."

Avantages pilotés par la configuration

  • Séparation des préoccupations : Configuration distincte de la logique de déploiement.
  • Promotion de l’environnement : Même script avec différents fichiers de configuration pour le développement, la préproduction, la production.
  • Contrôle de version : Suivez les modifications de configuration en même temps que le code.
  • Validation: Validez la structure de configuration avant le déploiement.

Supprimer des ressources Azure avec des scripts

Lors de la création et du test de scripts, n’oubliez pas de supprimer les ressources de test pour éviter les coûts inutiles. L'utilisation de scripts avec Azure CLI permet de nettoyer les ressources de manière sécurisée et cohérente.

Supprimer des comptes de stockage par date de création

Supprimez tous les comptes de stockage créés à ou après une date et une heure spécifiques :

#!/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

Supprimer des groupes de ressources avec la fonctionnalité de journalisation

Supprimez des groupes de ressources correspondant à un modèle d’affectation de noms avec une journalisation complète :

#!/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

Pratiques de suppression sécurisée

Implémentez des vérifications de sécurité avant la suppression :

#!/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."

Avertissement

Vérifiez toujours les ressources avant la suppression. La suppression d’un groupe de ressources supprime définitivement toutes les ressources qu’elle contient. Cette opération est irréversible.

Meilleures pratiques pour les scripts de production

Les scripts Azure CLI prêts pour la production nécessitent une planification et une implémentation minutieuses. Les scripts fournis dans cette unité illustrent des concepts fondamentaux, mais les environnements de production exigent des considérations supplémentaires.

Composants de script de production essentiels

Un script Azure CLI robuste pour les environnements de production doit inclure :

1. Authentification et autorisation :

#!/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. Gestion complète des erreurs :

#!/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. Vérifications de l’Idempotency :

#!/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. Journalisation structurée :

#!/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. Validation de la configuration :

#!/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. Stratégie d’étiquetage :

#!/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. Fonctionnalité de restauration :

#!/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

Structure de l’organisation de script

Organisez des scripts en modules logiques :

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

Bibliothèque de fonctions réutilisables

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
}

Tester des scripts en toute sécurité

Tester des scripts dans des environnements isolés :

#!/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"

Exécutez en mode test :

TEST_MODE=true ./deploy-infrastructure.sh

Ressources supplémentaires

Les scripts de cette unité illustrent les fonctionnalités d’Azure CLI lors de la combinaison de variables, de boucles, de conditions et de gestion des erreurs avec des commandes Azure CLI . Ces modèles s’étendent de la gestion de quelques ressources à l’orchestration de déploiements d’infrastructure d’entreprise complexes.