Can I register a domain in aws with terraform?

8.1k views Asked by At

I didn't find anything useful in the docs. Can this be done with terraform somehow?

3

There are 3 answers

2
John Rotenstein On BEST ANSWER

Registering a domain name involves a commitment from a Domain Name Registrar for a minimum of 12 months.

Tools like Terraform and AWS CloudFormation are used to create, update and delete infrastructure such as networks, EC2 instances and database.

While AWS does offer the ability to register domain names (which is done through gandi.net), this is not something you would do with tools like Terraform because you cannot simply "unregister" a domain name.

You could choose to use such tools to add sub-domains to an existing domain. They just aren't appropriate for the initial purchase of a domain name.

0
sam-6174 On

Below is a custom module to accomplish this using the AWS cli.


README.md

Description

This module will check if the AWS domain is already registered in your AWS account.

If the domain is not registered to your account then it will attempt to register it.

It will then validate that it was registered successfully.

Note

The registrant info provided by the variables is used only when registering the domain.

After the domain is registered, this module makes no attempt to keep the registration properties in sync.

This means that any changes will go undetected if you (1) register the domain and then (2) modify the registrant variables, e.g. organization_name.

Validation

When you register a new domain, you will need to confirm it via the email you provided.

After registration, you should receive an email from AWS with subject Verify your email address for <domain_name>

The domain will be registered immediately, and you have 14 days to verify the email before losing the domain.


variables.tf

variable "name" {
  description = "Name of the project, e.g. my-project"
  type        = string
}

variable "environment" {
  description = "The environment, e.g. prod"
  type        = string
}

variable "aws_region" {
  description = "'This command runs only in the us-east-1 region' - https://awscli.amazonaws.com/v2/documentation/api/latest/reference/route53domains/register-domain.html"
  type        = string
  default     = "us-east-1"
}

variable "auto_renew" {
  description = "Auto renew the domain when it expires, e.g. true"
  type        = bool
  default     = true
}

variable "duration_years" {
  description = "Number of years until the domain expires, e.g. 1"
  type        = number
  default     = 1
}

variable "privacy_protect_contact" {
  description = "Hide your contact details in the WHOIS record, e.g. true"
  type        = bool
  default     = true
}

variable "domain_name" {
  description = "The domain name to register, e.g. website.com"
  type        = string
}

variable "first_name" {
  description = "The first name for the registrar contact"
  type        = string
}

variable "last_name" {
  description = "The last name for the registrar contact"
  type        = string
}

variable "organization_name" {
  description = "The organization name for the registrar contact"
  type        = string
}

variable "address_line_1" {
  description = "The address for the registrar contact, e.g. 1 Main Street"
  type        = string
}

variable "city" {
  description = "The city for the registrar contact, e.g. New York City"
  type        = string
}

variable "state" {
  description = "The state for the registrar contact, e.g. NY"
  type        = string
}

variable "country_code" {
  description = "The country_code for the registrar contact, e.g. US"
  type        = string
}

variable "zip_code" {
  description = "The zip_code for the registrar contact, e.g. 10001"
  type        = string
}

variable "phone" {
  description = "The phone for the registrar contact, e.g. +1.8005551212"
  type        = string
}

variable "email" {
  description = "The email for the registrar contact, e.g. [email protected]"
  type        = string
}

main.tf

locals {
  cli_input_json_path = "${path.module}/cli_input_json/${var.name}-${var.environment}.json"
}

resource "local_file" "cli_input_json" {
  filename = local.cli_input_json_path
  content = <<-EOF
    {
      "DomainName": "${var.domain_name}",
      "DurationInYears": ${var.duration_years},
      "AutoRenew": ${var.auto_renew},
      "PrivacyProtectAdminContact": ${var.privacy_protect_contact},
      "PrivacyProtectRegistrantContact": ${var.privacy_protect_contact},
      "PrivacyProtectTechContact": ${var.privacy_protect_contact},
      "AdminContact": {
          "ContactType": "PERSON",
          "FirstName": "${var.first_name}",
          "LastName": "${var.last_name}",
          "OrganizationName": "${var.organization_name}",
          "AddressLine1": "${var.address_line_1}",
          "City": "${var.city}",
          "State": "${var.state}",
          "CountryCode": "${var.country_code}",
          "ZipCode": "${var.zip_code}",
          "PhoneNumber": "${var.phone}",
          "Email": "${var.email}"
      },
      "RegistrantContact": {
          "ContactType": "PERSON",
          "FirstName": "${var.first_name}",
          "LastName": "${var.last_name}",
          "OrganizationName": "${var.organization_name}",
          "AddressLine1": "${var.address_line_1}",
          "City": "${var.city}",
          "State": "${var.state}",
          "CountryCode": "${var.country_code}",
          "ZipCode": "${var.zip_code}",
          "PhoneNumber": "${var.phone}",
          "Email": "${var.email}"
      },
      "TechContact": {
          "ContactType": "PERSON",
          "FirstName": "${var.first_name}",
          "LastName": "${var.last_name}",
          "OrganizationName": "${var.organization_name}",
          "AddressLine1": "${var.address_line_1}",
          "City": "${var.city}",
          "State": "${var.state}",
          "CountryCode": "${var.country_code}",
          "ZipCode": "${var.zip_code}",
          "PhoneNumber": "${var.phone}",
          "Email": "${var.email}"
      }
    }
  EOF
}

resource "null_resource" "aws_register_domain" {
  provisioner "local-exec" {
    command     = "${path.module}/aws_register_domain.sh"
    environment = {
      aws_region = var.aws_region
      domain_name = var.domain_name
      cli_input_json_path = local.cli_input_json_path
    }
  }

  depends_on = [
    local_file.cli_input_json
  ]
}

aws_register_domain.sh

#!/usr/bin/env bash
set -e


##
# Validate expected environment variables
##

if [[ -z "$aws_region" ]]; then
  echo 'aws_register_domain: no value for $aws_region' >&2
  exit 1
fi

if [[ -z "$domain_name" ]]; then
  echo 'aws_register_domain: no value for $domain_name' >&2
  exit 1
fi

if [[ -z "$cli_input_json_path" ]]; then
  echo 'aws_register_domain: no value for $cli_input_json_path' >&2
  exit 1
fi


##
# Check if domain is already registered to our account
##

echo "aws_register_domain: Checking if domain $domain_name is already registered in this AWS account"

registration_check=$(
  (
    aws route53domains get-domain-detail \
      --region "$aws_region" \
      --domain-name "$domain_name" \
      2>&1 \
  ) || :
)

# https://stackoverflow.com/a/16951928
re_escape() {
  sed 's/[][\.|$(){}?+*^]/\\&/g' <<< "$*"
}

domain_name_escaped=`re_escape "$domain_name"`
already_exists=`((echo "$registration_check" | grep -Eq 'Domain '"$domain_name_escaped"' not found in [0-9]+ account') && echo 'no') || echo 'yes'`

if [[ "$already_exists" == 'yes' ]]; then
  echo "aws_register_domain: Domain $domain_name is already registered in this AWS account"
  exit 0
elif [[ "$already_exists" == 'no' ]]; then
  found_domain_name=`echo "$registration_check" | jq -r '.DomainName'`
  if [[ "$found_domain_name" != "$domain_name" ]]; then
    echo "aws_register_domain: Expected found_domain_name to be '$domain_name' but found '$found_domain_name'" >&2
    exit 1
  fi
else
  echo "aws_register_domain: Expected already_exists to be 'yes' or 'no' but found '$already_exists'" >&2
  exit 1
fi

##
# Register the domain
##

echo "aws_register_domain: Attempting to register domain defined in '$cli_input_json_path'"

operation_id=$(
  aws route53domains register-domain \
    --region "$aws_region" \
    --cli-input-json "file://$cli_input_json_path" \
  | jq -r '.OperationId'
)

if [[ -z "$operation_id" ]]; then
  echo 'aws_register_domain: no OperationId returned' >&2
  exit 1
fi

echo "aws_register_domain: Pending registration's OperationId = $operation_id"


##
# Wait while domain registration is IN_PROGRESS
##

while true; do
  operation_info=$(
    aws route53domains get-operation-detail \
      --region "$aws_region" \
      --operation-id "$operation_id"
  )

  operation_status=`echo "$operation_info" | jq -r '.Status'`
  if [[ "$operation_status" != 'IN_PROGRESS' ]]; then
    break
  fi

  sleep 10
end


##
# Validate successful domain registration
##

echo "aws_register_domain: Registration finished with $operation_info"

expected_status='SUCCESSFUL'
if [[ "$operation_status" != "$expected_status" ]]; then
  echo "aws_register_domain: Expected final status of '$expected_status' but found '$operation_status'" >&2
  exit 1
fi
0
Skaronator On

The AWS provider in Terraform now supports registering and managing of domains: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53domains_registered_domain

resource "aws_route53domains_registered_domain" "example" {
  domain_name = "example.com"

  name_server {
    name = "ns-195.awsdns-24.com"
  }

  name_server {
    name = "ns-874.awsdns-45.net"
  }

  tags = {
    Environment = "test"
  }
}

Make sure to read the fine print on top of the documentation page. This resource behaves differently when deleted.