Azure VMのIPはNICからパブリックIP(PIP)の関連付けを解除→もう一度割当てを行うと別のIPになるので、
これを利用してIPをローテートする小技。
azure-cliではNICからPIPの関連付け解除ができないので、直接RESTを叩いています。
rotate-pip.sh
#!/bin/bash
# VM情報
: ${SUBSCRIPTION_ID:?}
: ${RESOURCE_GROUP_NAME:?}
: ${NIC_NAME:?}
: ${PIP_NAME:?}
: ${VNET_NAME:?}
: ${VNET_SUBNET_NAME:="Subnet"}
: ${IPCONFIG_NAME:="ipconfig1"}
# アプリケーション認証情報
: ${CLIENT_ID:?}
: ${TENANT_ID:?}
: ${CLIENT_SECRET:?}
type jq > /dev/null || exit 1
# -------------
# OAuthトークン取得
AUTHORIZATION_STRING="Bearer $(curl -sXPOST -H "Content-Type: application/x-www-form-urlencoded" "https://login.windows.net/${TENANT_ID}/oauth2/token" -d "grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&resource=https://management.core.windows.net/" | jq -r '.access_token')"
NIC_ENTRYPOINT="https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.Network/networkInterfaces/${NIC_NAME}?api-version=2016-03-30"
PIP_ENTRYPOINT="https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.Network/publicIPAddresses/${PIP_NAME}?api-version=2016-03-30"
# NICにPUTするときのJSONのテンプレ
JSON_TEMPLATE=$(cat <<EOT
{
"name": "${NIC_NAME}",
"location": "japaneast",
"properties": {
"ipConfigurations": [
{
"name": "${IPCONFIG_NAME}",
"id": "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.Network/networkInterfaces/${NIC_NAME}/ipConfigurations/${IPCONFIG_NAME}",
"properties": {
"publicIPAddress": %s,
"subnet": {
"id":"/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.Network/virtualNetworks/${VNET_NAME}/subnets/${VNET_SUBNET_NAME}"
}
}
}
]
}
}
EOT
)
# NICへの変更リクエスト
request(){
curl -sXPUT -H "Content-Type: application/json" -H "Authorization: ${AUTHORIZATION_STRING}" "$NIC_ENTRYPOINT" -d "$1" -o /dev/null
}
# 現在のパブリックIPアドレスを取得
get_ip() {
curl -s -H "Authorization: ${AUTHORIZATION_STRING}" "${PIP_ENTRYPOINT}" | jq -r ".properties.ipAddress"
}
# NICの変更が完了するまで待つ
wait_for_completion() {
printf "wait for completion"
until [[ "$(curl -s -H "Authorization: ${AUTHORIZATION_STRING}" "${NIC_ENTRYPOINT}" | jq -r ".properties.provisioningState")" != "Updating" ]]
do
sleep 15
printf "."
done
echo
}
# -------------
OLD_IP="$(get_ip)"
echo "OLD_IP: $OLD_IP"
# PIP取り外し
echo "remove PIP"
JSON="$(printf "$JSON_TEMPLATE" null)"
request "$JSON"
wait_for_completion
# すぐ振り直すと同じIPが付くので少し待つ
echo "wait 30 seconds for rotate"
sleep 30
# PIP付け直し
echo "set new PIP"
PIP="{\"id\":\"/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Network/publicIPAddresses/${PIP_NAME}\"}"
JSON="$(printf "$JSON_TEMPLATE" "$PIP")"
request "$JSON"
wait_for_completion
NEW_IP="$(get_ip)"
echo "NEW_IP: $NEW_IP"
# 変わってなかったら失敗扱い
[[ "$OLD_IP" != "$NEW_IP" ]] || exit 1