TL;DR
- Azure предоставляет deployment what-if для предварительной валидации — используйте в CI перед каждым apply
- Azurite эмулирует Storage, Queues и Tables локально — быстрее реального Azure для storage-тестов
- Ошибка #1: пропуск тестирования Azure Policy до тех пор, пока деплой не упадёт в продакшене
Подходит для: Команд, деплоящих в Azure с Terraform, Bicep или ARM templates Пропустить если: Вы только на AWS/GCP или используете Azure PaaS без инфраструктурного кода Время чтения: 10 минут
Тестирование инфраструктуры Azure: Terraform, Bicep и локальная эмуляция — критически важная дисциплина в современном обеспечении качества программного обеспечения. According to Gartner, worldwide cloud spending will exceed $1 trillion by 2025, making cloud testing skills essential (Gartner Cloud Forecast). According to HashiCorp’s 2024 State of Cloud Strategy survey, 78% of organizations use a multi-cloud strategy (HashiCorp State of Cloud 2024). Это руководство охватывает практические подходы, которые QA-команды могут применить немедленно: от базовых концепций и инструментов до реальных паттернов реализации. Независимо от того, развиваешь ли ты навыки в этой области или улучшаешь существующий процесс, здесь ты найдёшь действенные техники, подкреплённые практическим опытом. Цель — не просто теоретическое понимание, а рабочий фреймворк, который можно адаптировать под контекст команды, технологический стек и цели по качеству.
Реальная проблема
Azure вводит вызовы тестирования, отличные от AWS:
Azure Policy: Корпоративные Azure-подписки имеют policies, блокирующие несоответствующие деплои. Вы не узнаете о нарушениях пока terraform apply или az deployment не упадёт.
Регистрация Resource Provider: Первое использование сервиса в подписке требует регистрации provider. Тесты неожиданно падают в чистых подписках.
Задержки распространения Azure AD: Service principals, managed identities и role assignments требуют времени для распространения. Тесты, работающие локально, падают в CI.
Ограничения имён: Именование ресурсов Azure имеет сложные правила — storage accounts должны быть глобально уникальными, 3-24 строчных буквенно-цифровых символа. Key Vaults имеют другие правила. VMs имеют другие правила опять.
«Тестирование в облаке без контроля затрат — это катастрофа для бюджета. Всегда настраивай алерты на расходы перед запуском нагрузочных тестов на облачной инфраструктуре — я видел, как команды сжигали тысячи за один прогон.» — Юрий Кан, Senior QA Lead
Deployment What-If
Операция what-if Azure валидирует деплои перед выполнением:
# ARM/Bicep what-if
az deployment group what-if \
--resource-group myResourceGroup \
--template-file main.bicep \
--parameters @params.json
# Деплой на уровне подписки
az deployment sub what-if \
--location eastus \
--template-file main.bicep
Для Terraform комбинируйте plan с Azure-специфичной валидацией:
# Генерировать plan
terraform plan -out=tfplan
# Конвертировать в JSON для анализа
terraform show -json tfplan > tfplan.json
# Проверить соответствие Azure Policy (требует Azure CLI)
az policy state trigger-scan --resource-group myResourceGroup
# Или использовать Checkov с Azure правилами
checkov -f tfplan.json --framework terraform_plan
Terratest для Azure
Terratest имеет Azure-специфичные модули:
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/azure"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestAzureStorageAccount(t *testing.T) {
t.Parallel()
subscriptionID := azure.GetSubscriptionID()
uniqueID := random.UniqueId()
terraformOptions := &terraform.Options{
TerraformDir: "../modules/storage-account",
Vars: map[string]interface{}{
"resource_group_name": "rg-test-" + uniqueID,
"storage_account_name": "sttest" + uniqueID,
"location": "eastus",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Получить outputs
resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name")
storageAccountName := terraform.Output(t, terraformOptions, "storage_account_name")
// Проверить что storage account существует и имеет правильные свойства
exists := azure.StorageAccountExists(t, storageAccountName, resourceGroupName, subscriptionID)
assert.True(t, exists)
// Проверить свойства storage account
storageAccount := azure.GetStorageAccount(t, storageAccountName, resourceGroupName, subscriptionID)
assert.Equal(t, "Standard_LRS", string(storageAccount.Sku.Name))
assert.True(t, *storageAccount.EnableHTTPSTrafficOnly)
}
func TestAzureVirtualNetwork(t *testing.T) {
t.Parallel()
subscriptionID := azure.GetSubscriptionID()
uniqueID := random.UniqueId()
terraformOptions := &terraform.Options{
TerraformDir: "../modules/virtual-network",
Vars: map[string]interface{}{
"resource_group_name": "rg-test-" + uniqueID,
"vnet_name": "vnet-test-" + uniqueID,
"address_space": []string{"10.0.0.0/16"},
"location": "eastus",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vnetName := terraform.Output(t, terraformOptions, "vnet_name")
resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name")
// Проверить что VNet существует
exists := azure.VirtualNetworkExists(t, vnetName, resourceGroupName, subscriptionID)
assert.True(t, exists)
// Проверить subnets
subnets := azure.GetVirtualNetworkSubnets(t, vnetName, resourceGroupName, subscriptionID)
assert.GreaterOrEqual(t, len(subnets), 1)
}
Azurite для локального тестирования Storage
Azurite эмулирует сервисы Azure Storage локально:
# Установить через npm
npm install -g azurite
# Запустить все сервисы
azurite --silent --location ./azurite-data --debug ./azurite-debug.log
# Или через Docker
docker run -d \
-p 10000:10000 \
-p 10001:10001 \
-p 10002:10002 \
-v azurite-data:/data \
mcr.microsoft.com/azure-storage/azurite
Настроить Terraform для использования Azurite:
provider "azurerm" {
features {}
# Для Azurite переопределить storage endpoints
# Примечание: Полный AzureRM не поддерживает Azurite напрямую
# Используйте этот паттерн для тестирования кода приложения, не полного Terraform
}
# Для тестирования приложения со storage
resource "null_resource" "test_storage" {
provisioner "local-exec" {
command = <<-EOT
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1"
python test_storage_operations.py
EOT
}
}
Тесты на Python с Azurite:
import os
from azure.storage.blob import BlobServiceClient
def test_blob_operations():
# Строка подключения Azurite
connection_string = os.environ.get(
"AZURE_STORAGE_CONNECTION_STRING",
"DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1"
)
blob_service = BlobServiceClient.from_connection_string(connection_string)
# Создать container
container_client = blob_service.create_container("test-container")
# Загрузить blob
blob_client = container_client.get_blob_client("test-blob.txt")
blob_client.upload_blob("Hello, Azure!", overwrite=True)
# Скачать и проверить
downloaded = blob_client.download_blob().readall()
assert downloaded == b"Hello, Azure!"
# Очистка
container_client.delete_container()
Тестирование Bicep с What-If
Для Bicep деплоев интегрируйте what-if в CI:
# azure-pipelines.yml
trigger:
paths:
include:
- infra/**
stages:
- stage: Validate
jobs:
- job: BicepValidation
pool:
vmImage: ubuntu-latest
steps:
- task: AzureCLI@2
displayName: 'Bicep Lint'
inputs:
azureSubscription: 'MyServiceConnection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az bicep build --file infra/main.bicep --stdout > /dev/null
- task: AzureCLI@2
displayName: 'What-If Analysis'
inputs:
azureSubscription: 'MyServiceConnection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az deployment group what-if \
--resource-group $(ResourceGroup) \
--template-file infra/main.bicep \
--parameters infra/params.$(Environment).json
- stage: Deploy
dependsOn: Validate
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployInfra
environment: production
strategy:
runOnce:
deploy:
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'MyServiceConnection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az deployment group create \
--resource-group $(ResourceGroup) \
--template-file infra/main.bicep \
--parameters infra/params.$(Environment).json
Тестирование Azure Policy
Тестируйте соответствие Azure Policy перед деплоем:
func TestAzurePolicyCompliance(t *testing.T) {
t.Parallel()
subscriptionID := azure.GetSubscriptionID()
terraformOptions := &terraform.Options{
TerraformDir: "../modules/storage-account",
Vars: map[string]interface{}{
"resource_group_name": "rg-policy-test",
"storage_account_name": "stpolicytest" + random.UniqueId(),
// Намеренно несоответствующий для тестирования
"enable_https_only": false,
},
}
// Не авто-удалять - хотим проверить состояние policy
terraform.Init(t, terraformOptions)
// Plan должен успешно выполниться
terraform.Plan(t, terraformOptions)
// Но apply должен упасть из-за policy
_, err := terraform.ApplyE(t, terraformOptions)
// Утверждаем что ошибка связана с policy
assert.Error(t, err)
assert.Contains(t, err.Error(), "PolicyViolation")
// Очистить неудачный деплой
terraform.Destroy(t, terraformOptions)
}
Запросить соответствие policy программно:
# Запустить оценку policy
az policy state trigger-scan --resource-group myResourceGroup
# Проверить состояние соответствия
az policy state list \
--resource-group myResourceGroup \
--filter "complianceState eq 'NonCompliant'" \
--query "[].{Resource:resourceId, Policy:policyDefinitionName}"
Интеграция CI/CD
GitHub Actions для Azure инфраструктуры:
name: Azure Infrastructure
on:
pull_request:
paths:
- 'terraform/**'
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: terraform
- name: Terraform Validate
run: terraform validate
working-directory: terraform
- name: Terraform Plan
id: plan
run: terraform plan -out=tfplan -no-color
working-directory: terraform
continue-on-error: true
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: terraform/
framework: terraform
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Plan 📖
\`\`\`
${{ steps.plan.outputs.stdout }}
\`\`\`
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
terratest:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run Terratest
run: go test -v -timeout 30m ./tests/...
env:
ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_USE_OIDC: true
Подходы с помощью ИИ
Azure имеет сложные правила именования и взаимодействия policy. ИИ-инструменты помогают навигировать.
Что ИИ делает хорошо:
- Генерация соответствующих имён ресурсов для Azure naming conventions
- Перевод определений Azure Policy в assertions тестов
- Создание кода Terratest из спецификаций Azure ресурсов
- Объяснение Azure-специфичных сообщений об ошибках и решений
Что всё ещё требует людей:
- Понимание организационных требований Azure Policy
- Проектирование архитектуры тестов для сложных Azure Landing Zones
- Решение какие тесты нужны с реальным Azure vs локальная эмуляция
- Отладка проблем timing распространения Azure AD
Полезный промпт:
У меня есть Azure Terraform модуль который создаёт:
- Resource Group
- Storage Account с blob containers
- Key Vault с access policies
- Azure Functions с managed identity
Сгенерируй:
1. Код Terratest для валидации всех ресурсов
2. Проверки Azure Policy которые нужно включить
3. Типичные Azure-специфичные подводные камни для тестирования
4. Тесты Azurite для операций со storage
Когда это не работает
Тестирование Azure инфраструктуры имеет ограничения:
Timing Azure AD: Role assignments и распространение managed identity могут занимать минуты. Тестам нужна логика retry и задержки.
Региональные различия: Некоторые сервисы недоступны во всех регионах. Тесты работающие в eastus падают в других регионах.
Ресурсы уровня подписки: Management groups, подписки и некоторые policies требуют повышенных прав которых CI service principals могут не иметь.
Стоимость очистки: Неудачные Terraform destroys оставляют осиротевшие ресурсы. У Azure нет таких же инструментов очистки как у AWS.
Рассмотрите дополнительные подходы:
- Мульти-облачное тестирование для переносимых паттернов
- Azure DevTest Labs для изолированных тестовых окружений
- Policy as Code для предварительной валидации
Framework принятия решений
Используйте Azurite когда:
- Тестируете код приложения использующий Azure Storage
- Скорость критична (Azurite мгновенный)
- Требуется сетевая изоляция
Используйте what-if когда:
- Валидация Bicep/ARM деплоев
- Проверка соответствия Azure Policy
- Предварительный просмотр изменений
Используйте Terratest с реальным Azure когда:
- Тестируете полные модули инфраструктуры
- Валидация cross-resource интеграций
- Финальная валидация перед продакшеном
Измерение успеха
| Метрика | До | После | Как отслеживать |
|---|---|---|---|
| Сбои Azure Policy в CI | Частые | 0 | Логи деплоя |
| Время выполнения тестов | 20+ мин | <10 мин | Метрики CI |
| Осиротевшие тестовые ресурсы | Неизвестно | 0 | Azure Cost Management |
| Успешность первого деплоя | 60% | 95%+ | История деплоев |
Предупреждающие знаки что не работает:
- what-if проходит но deploy падает
- Нестабильные тесты из-за timing Azure AD
- Растущий список задач ручной очистки
- Команды обходят CI для “быстрых” деплоев
Что дальше
Начните с валидации, затем расширяйтесь до интеграции:
- Добавьте
az deployment what-ifв каждый PR - Внедрите Checkov для сканирования Azure policy
- Настройте Azurite для локального тестирования storage
- Добавьте Terratest для критичных модулей инфраструктуры
- Настройте автоматизацию очистки для упавших тестов
- Отслеживайте Azure-специфичные метрики тестирования
Цель — ловить Azure-специфичные проблемы до деплоя, а не после.
Связанные статьи:
- Тестирование AWS инфраструктуры с LocalStack
- Тестирование мульти-облачной инфраструктуры
- Policy as Code Testing: OPA vs Sentinel
- Стратегии тестирования и валидации Terraform
Внешние ресурсы:
Официальные ресурсы
FAQ
В чём разница между тестированием в облаке и тестированием облака? Тестирование в облаке использует облачную инфраструктуру как тестовое окружение. Тестирование облака проверяет, что твои облачные ресурсы, конфигурации и IaC-шаблоны работают правильно.
Как тестировать шаблоны CloudFormation или Terraform? Используй cfn-lint/tflint для статического анализа, LocalStack или AWS SAM для локального тестирования и интеграционные тесты, разворачивающие в staging-аккаунт и проверяющие состояние ресурсов.
Каковы риски затрат при тестировании в облаке? Неконтролируемые нагрузочные тесты, забытые тестовые ресурсы и стоимость передачи данных могут генерировать неожиданные счета. Всегда устанавливай алерты на бюджет и используй теги ресурсов.
Как тестировать мультиоблачные архитектуры? Тестируй каждое облако независимо с провайдер-специфичными инструментами, затем тестируй точки интеграции между облаками. Используй абстракционные слои как Terraform для единообразных паттернов.
See Also
- Тестирование Сетевой Конфигурации: Batfish, Terraform и Валидация VPC для Облачной Инфраструктуры - Освойте тестирование сетевой конфигурации с Batfish для…
- Тестирование Масштабируемости Инфраструктуры: Валидация Auto-Scaling с K6, Locust и Terraform - Освойте тестирование масштабируемости инфраструктуры с K6, Locust…
- Тестирование Security Groups: Валидация AWS Security Groups, Azure NSGs и Правил Firewall GCP - Освойте тестирование security groups в AWS, Azure и GCP с Checkov,…
- Тестирование оценки затрат для Infrastructure as Code: Полное руководство - Освойте тестирование оценки затрат для IaC с Infracost, анализом…
