Sprawdzenie ilości danych do importu na pierwszy rzut oka przyprawia o zawrót głowy. Nie mówiąc już nawet o ilości możliwych błędów.
Potrzebna jest automatyzacja z wykorzystaniem:
- AWS CLI
- Python 3.9
- Terraform
Założenia:
- Na tym etapie nie przewiduje współpracy między stanami TF więc output zostaje pominięty.
- Każda strefa Route53 zostanie zaimportowana oddzielnie, jako oddzielny moduł.
- Korzystam z procedury opisanej przez AWS: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-migrating.html#hosted-zones-migrating-create-file
Szybki przegląd Google wskazał: https://mrkaran.dev/posts/terraform-route53-import/. Obiecujące, potrzebuje drobnych modyfikacji.
Oryginalny post opisuje użycie terraform import, tutaj z kolei jest potrzebne wygenerowanie pełnego szablonu resource.
Kroki:
- Wygenerowanie pliku JSON z istniejącymi rekordami w strefie. Zrobi to AWS CLI.
aws route53 list-resource-record-sets --hosted-zone-id ZONE ID > records.jsonW efekcie powstaje plikrecords.json, który zawiera wszystkie informacje o strefie Route53 włącznie z wygenerowanymi automatycznie rekordami NS i SOA. - Uruchomienie skryptu:
generate_zone_records_from_json.py- kod podany poniżej.
Skrypt ten wygeneruje plik.tfktóry można wykorzystać do budowania nowej strefy Route 53. Należy ręcznie usunąć rekordy NS i SOA dla głównej domeny z wygenerowanego pliku tf - te wpisy AWS generuje automatycznie przy tworzeniu nowej strefy DNS. Skrypt nie usuwa ich automatycznie gdyż to by mogło prowadzić do pominięcia innych, ważnych rekordów NS - na przykład delegacji subdomen.
"""
Generate zone records file (terraform) from zone dump.
To get zone dump:
aws route53 list-resource-record-sets --hosted-zone-id ZONE ID > records.json
Example usage:
ZONE_FILE=records.json TERRAFORM_DIR=zone_name python generate_zone_records_from_json.py
Based on:
https://mrkaran.dev/posts/terraform-route53-import/
"""
import json
from os import getenv, path
from string import Template
from subprocess import run
from sys import exit
# terraform resource path of the zone to add resource to.
ZONE_ID = "aws_route53_zone.main.zone_id"
ZONE_FILE = getenv("ZONE_FILE")
TERRAFORM_DIR = getenv("TERRAFORM_DIR")
RECORDS_FILE_PATH = path.join(TERRAFORM_DIR, "records.tf")
def check_env_vars():
if not ZONE_FILE:
return "$ZONE_FILE"
if not TERRAFORM_DIR:
return "$TERRAFORM_DIR"
return ""
# Loads the zone records in a dict
def load_records(zone_file=ZONE_FILE):
with open(zone_file) as record_file:
data = json.load(record_file)
return data
# Creates the Terraform template
def template_records_file(resource_safe_name, resource_name, resource_type, resource_values=None, alias_values=None, resource_ttl=60):
if resource_values:
add_records_record = Template(
"""
resource "aws_route53_record" "$resource_safe_name" {
zone_id = $zone_id
name = "$resource_name"
type = "$resource_type"
ttl = "$resource_ttl"
records = $resource_values
}
"""
)
if alias_values:
add_records_record = Template(
"""
resource "aws_route53_record" "$resource_safe_name" {
zone_id = $zone_id
name = "$resource_name"
type = "$resource_type"
alias {
name = "$aliasTarget_DNSName"
zone_id = "$aliasTarget_HostedZoneId"
evaluate_target_health = $aliasTarget_EvaluateTargetHealth
}
}
"""
)
with open(RECORDS_FILE_PATH, "a") as f:
f.write(add_records_record.safe_substitute(
zone_id=ZONE_ID,
resource_name=resource_name,
resource_safe_name=resource_safe_name,
resource_type=resource_type,
resource_ttl=resource_ttl,
resource_values=resource_values,
aliasTarget_DNSName=alias_values.get("DNSName"),
aliasTarget_HostedZoneId=alias_values.get("HostedZoneId"),
aliasTarget_EvaluateTargetHealth="true" if alias_values.get(
"EvaluateTargetHealth") else "false"
))
if __name__ == "__main__":
missing = check_env_vars()
if missing:
exit(f"Required env variable {missing} is missing.")
with open(RECORDS_FILE_PATH, "w") as f:
# just clear the file
pass
records = load_records()
for i in records.get("ResourceRecordSets"):
resource_name = i.get("Name")
resource_type = i.get("Type")
resource_safe_name = resource_name.replace(".", "__")[:-2]
resource_safe_name = f"{resource_type}_{resource_safe_name}"
resource_ttl = i.get("TTL")
alias_values = i.get("AliasTarget", {})
resource_values = json.dumps(
[record.get("Value").replace("\"", "") for record in i.get("ResourceRecords", [])])
template_records_file(resource_safe_name, resource_name,
resource_type, resource_values, alias_values, resource_ttl)
print(f"Imported {resource_name} {resource_type}")3. Pozostaje dodać plik to już istniejącego kodu terraform.
4. Idąc dalej wg instrukcji AWS, https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-migrating.html#hosted-zones-migrating-create-file należy:
- przygotować nową strefę route 53 wg wygenerowanych plików
- zaktualizować ustawienia NS dla domeny - powinny teraz wskazywać nowo utworzoną strefę
- Poczekać conajmniej 48 godzin przed wykonywaniem jakichkolwiek zmian w obydwu strefach Route53.
- Zweryfikować aktualizację rekordów DNS
dig -t NS domenapowinno zwrócić NSy nowej strefy. - Dopiero po tej weryfikacji można usunąć starą strefę Route 53.
Masz uwagi do posta, chcesz porozmawiać, szukasz pomocy z Terraform? Napisz do mnie!
Ps. Spodobał Ci się post? Udostępnij go na swoich kanałach.