Azure: управление зонами в Azure DNS — Git, GoCD, bash и Azure CLI

Автор: | 05/12/2016
 

azure_logoНовый проект переносит свои домены на DNS Azure, для чего требовалось создать возможность управления ими.

Схема получается следующая:

  • файлы зон хранятся в Stash;
  • GoCD проверяет обновления в Stash, и триггерит билд;
  • во время билда — файлы зон заливаются на Azure DNS.

Обновление данных выполняется bash-скриптом с помощью Azure CLI.

Больше про Azure CLI  и работу с Azure DNS можно найти в посте Azure: работа с DNS из Azure CLI.

Stash репозиторий

Репозиторий выглядит так:

$ tree azure-dns/
azure-dns/
├── scripts
│   └── dns_update.sh
├── zone-backup
│   └── domain.ms.txt
└── zone-files
    └── domain.ms.txt
  • каталог zone-files: содержит файлы зон, которые могут быть отредактированы командой проекта;
  • каталог zone-backup: при каждом билде — скрипт с помощью Azure CLI сохраняет текущие файлы зон сюда;
  • каталог scripts: скрипты 🙂

Файлы зон — это обычные txt-файлы в том виде, как их можно увидеть на BIND-сервере, например:

$ cat zone-files/domain.ms.txt 
; Exported zone file from Azure DNS
; Resource Group Name: DNS
; Zone name: domain.ms
; Date and time (UTC): Fri Mar 25 2016 11:42:14 GMT+0000

$TTL 3600
$ORIGIN domain.ms.

@ 3600 IN SOA ns1-02.azure-dns.com. msnhst.microsoft.com. (
                2016032514
                3600
                300
                2419200
                300
                )

@ 3600 IN A 104.40.183.236
@ 3600 IN MX 10 mx.domain.net.

@ 172800 IN NS ns1-02.azure-dns.com.
 172800 IN NS ns2-02.azure-dns.net.
 172800 IN NS ns3-02.azure-dns.org.
 172800 IN NS ns4-02.azure-dns.info.

www 3600 IN CNAME domain.ms.
test 3600 IN CNAME domain.ms.

GoCD билд

В GoCD имеется pipeline:

jager_dns_1

Который состоит из трех шагов:

jager_dns_3

  1. DNSverify: вызывается azure network dns zone import с опцией --parse-only, и в цикле проверяет все файлы зон из каталога zone-files;
  2. DNSbackup: использует azure network dns zone export для выгрузки все имеющихся на данный момент зон в каталог zone-backup;
  3. DNSupdate: собственно, обновление данных — с помощью azure network dns zone import каждый файл зоны из zone-files загружается в Azure DNS.

Для авторизации используется пользователь и пароль, которые передаются через Secure variables:

jager_dns_4

А так выглядит один из шагов билда:

jager_dns_5

Bash скрипт и Azure CLI

Основная часть всей схемы:

#!/usr/bin/env bash

# must be present from CI variables
USER=$AZURE_USER
PASS=$AZURE_PASS
SUB_ID=$SUBSCRIPTION_ID
# группа ресурсов
RES_GR="DNS"

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

# reset all options
# изврат делать это через массив - но выглядит приятнее
declare -A opts
opts=([verify]=0 [backup]=0 [import]=0)

if [ -z $* ]; then
    echo "No options found!"
    exit 1
fi

# скрипт принимает три опции, по кол-ву шагов в билде
# -v: verify zone-files before import
# -b: backup zones before import 
# -i: import zones to Azure DNS
while getopts "vbi" opt; do
    case $opt in
        v)
            opts[verify]=1
            ;;
        b)
            opts[backup]=1
            ;;
        i)
            opts[import]=1
            ;;
        *)  
            echo "No reasonable options found!"
            exit 0
            ;;
    esac
done

main () {

    # main() - используется как "инициализатор" Azure CLI
    # которому первым аргументом передается следующая функция для вызова
    echo -e "\n[$me] Azure CLI initialization started\n"

    # go to HOME and save curr path
    pushd $HOME &> /dev/null

    # "хак", что бы пропустить установку Azure CLI, если пакет уже установлен
    if [ $(node -p "require('azure-cli/package.json').version") ]; then
        echo -e "[$me] Azure CLI already installed, skip.\n"
    else
	    npm install azure-cli
    fi    
    
    export PATH="$PATH":"$HOME/node_modules/azure-cli/bin"

    # go back to /var/lib/go-agent-2/pipelines/AzureDNSupdate
    popd

    if azure login -v -u $USER -p $PASS; then
        azure account set -v $SUB_ID 
    else
        exit 1
    fi

    azure config mode arm
    azure provider register --namespace Microsoft.Network

    # after initialization - call function specified in first argument
    $1

}

dns_import () {

    echo -e "\n[$me] Azure DNS zones import started.\n"

    if [ -d zone-files ];then
        # create zone names from zone file name
        for i in $(ls -1 zone-files); do
            # получаем имя домена из имени файла, вырезая .txt
            zone=$(echo $i | sed 's/.txt//')
            # force используется, что бы не спрашивать при перезаписи зоны
            azure network dns zone import -v -g $RES_GR -n "$zone" -f zone-files/"$zone".txt --force
        done
    else
        echo -e "\nERROR: can not find zone files directory in $(pwd). Exit.\n"
        ls -l
        exit 1
    fi

}

dns_verify () {

    echo -e "\n[$me] Azure DNS zones verify started.\n"

    if [ -d zone-files ];then
        # create zone names from zone file name
        for i in $(ls -1 zone-files); do
            zone=$(echo $i | sed 's/.txt//')
            echo -e "[$me] Checking $zone..."
            if [ -e zone-files/"$zone".txt ]; then
                # grep "warn" or "error" from Azure CLI output
                # не нашел адекватного способа получить exit code, грепаем warn или error
                azure network dns zone import -v -g $RES_GR -n "$zone" --parse-only -f zone-files/"$zone".txt | grep "warn\|error" && {echo -e "\nERROR during parsing zone file $zone.txt. Exit." && exit 1} || echo "$zone check passed"
            else
                echo -e "\nERROR: can not find zone-files/"$zone".txt file. Exit.\n"
                exit 1
            fi
        done
        echo -e "\n[$me] All zone files checked successfully.\n"
    else
        echo -e "\nERROR: can not find zone files directory in $(pwd). Exit.\n"
        ls -l
        exit 1
    fi

}

dns_backup() {

    echo -e "\n[$me] Azure DNS zones backup started.\n"

    # получаем список всех доменов, которые сейчас есть на NS
    zones=$(azure network dns zone list -g $RES_GR --json | grep "name" | cut -d":" -f 2 | sed 's/"//g' | sed 's/,//' | tr -d " ")

    if [ -z $zones ]; then
        echo -e "WARNING: zones files not found. Skip.\n"
        exit 0
    fi

    for zone in $zones; do
        echo -e "Zone found: $zone.\n"
        # и выгружаем их в zone-backup
        if azure network dns zone export -q -v -g $RES_GR -n "$zone" -f zone-backup/"$zone".txt; then
            echo -e "\n$zone exported to zone-backup/"$zone".txt\n"
        else
            echo -e "\nERROR during $zone export. Exit.\n"
            exit 1
        fi
    done

    git add -A
    git commit -m "$(date +%Y%m%d) zones backup"
    git push origin
}

# проверяем полученные аргументы, и запускаем те, для которых значение == 1
# первой вызывается main(), которой первым аргументом передается следующая функция
# $act in ${!opts[@]} == "verify" or "backup" or "import"
for act in "${!opts[@]}"; do
    # i.e.: if ${oprts[verify]} == 1
    if [ "${opts[$act]}" == 1 ]; then
        # main() with first argument like "dns_verify()"
        main dns_$act
    fi
done

Сами билды выглядят так:

jager_dns_6

Ссылки по теме

Manage DNS records and record sets using CLI

Create DNS record sets and records using CLI

Import and export a DNS zone file

How to manage DNS Zones using CLI