Skip to content

Add Route53 hosted zones for all javaBin domains#107

Merged
Alexanderamiri merged 1 commit into
mainfrom
feat/dns-migration
Mar 26, 2026
Merged

Add Route53 hosted zones for all javaBin domains#107
Alexanderamiri merged 1 commit into
mainfrom
feat/dns-migration

Conversation

@Alexanderamiri
Copy link
Copy Markdown
Member

Summary

  • Creates Route53 hosted zones for java.no, javabin.no, javabin.com, and teknologihuset.no
  • Replicates all existing DNS records from Domeneshop (exported via their API)
  • Registrar stays at Domeneshop — only DNS management moves to Route53
  • No DNS cutover happens on merge — NS records at Domeneshop must be updated manually per domain

Records migrated

Domain Records Notes
java.no 18 Google Workspace MX, SPF, DMARC, DKIM (Mailchimp+SendGrid), wildcard A, GitHub Pages
javabin.no 5 MX → mail.java.no, DMARC, A/AAAA defaults
javabin.com 4 DMARC, A/AAAA defaults
teknologihuset.no 12 Google MX, Domeneshop email autodiscovery, CalDAV/CardDAV SRV, GitHub challenge

Cutover plan (post-merge)

  1. javabin.com — simplest, cut over first as validation
  2. javabin.no — simple, cut over second
  3. java.no — main org domain, cut over third
  4. teknologihuset.no — has Domeneshop email+hosting, cut over last

Each cutover = change NS records at Domeneshop to the Route53 nameservers from terraform output.

Test plan

  • CI plan shows 43 resources to add, 0 to change/destroy
  • After apply, verify each zone's NS records via aws route53 list-hosted-zones
  • Before each NS cutover, compare dig @<r53-ns> domain vs dig @ns1.hyp.net domain

Migrate DNS from Domeneshop to Route53 for all javaBin domains.
Registrar stays at Domeneshop — only DNS management moves to AWS.

Creates 4 hosted zones with all existing records:
- java.no: Google Workspace MX, SPF, DMARC, DKIM (Mailchimp/SendGrid),
  wildcard A, GitHub Pages subdomains
- javabin.no: MX forwarding to mail.java.no, DMARC
- javabin.com: DMARC only (+ Domeneshop default A/AAAA)
- teknologihuset.no: Google MX, Domeneshop email autodiscovery,
  CalDAV/CardDAV SRV records, GitHub org verification

After apply, NS records at Domeneshop need to be updated per domain
to complete the cutover. Migration order: javabin.com → javabin.no →
java.no → teknologihuset.no.
@Alexanderamiri Alexanderamiri requested a review from a team as a code owner March 26, 2026 22:43
@github-actions
Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 48 to add, 0 to change, 0 to destroy.

Plan output
Acquiring state lock. This may take a few moments...

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.cost_analytics.aws_bcmdataexports_export.cur will be created
  + resource "aws_bcmdataexports_export" "cur" {
      + id       = (known after apply)
      + tags_all = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }

      + export {
          + export_arn = (known after apply)
          + name       = "javabin-cur"

          + data_query {
              + query_statement      = "SELECT identity_line_item_id, identity_time_interval, bill_invoice_id, bill_invoicing_entity, bill_billing_entity, bill_bill_type, bill_payer_account_id, bill_billing_period_start_date, bill_billing_period_end_date, line_item_usage_account_id, line_item_line_item_type, line_item_usage_start_date, line_item_usage_end_date, line_item_product_code, line_item_usage_type, line_item_operation, line_item_availability_zone, line_item_resource_id, line_item_usage_amount, line_item_normalization_factor, line_item_normalized_usage_amount, line_item_currency_code, line_item_unblended_rate, line_item_unblended_cost, line_item_blended_rate, line_item_blended_cost, line_item_line_item_description, product_product_name, product_region, pricing_unit, pricing_public_on_demand_cost, pricing_public_on_demand_rate, pricing_term, pricing_offering_class, resource_tags_user_team, resource_tags_user_service, resource_tags_user_environment, resource_tags_user_repo, resource_tags_user_managed_by FROM COST_AND_USAGE_REPORT"
              + table_configurations = {
                  + "COST_AND_USAGE_REPORT" = {
                      + "INCLUDE_MANUAL_DISCOUNT_COMPATIBILITY" = "FALSE"
                      + "INCLUDE_RESOURCES"                     = "TRUE"
                      + "INCLUDE_SPLIT_COST_ALLOCATION_DATA"    = "FALSE"
                      + "TIME_GRANULARITY"                      = "DAILY"
                    }
                }
            }

          + destination_configurations {
              + s3_destination {
                  + s3_bucket = "javabin-cur-553637109631"
                  + s3_prefix = "cur"
                  + s3_region = "eu-central-1"

                  + s3_output_configurations {
                      + compression = "PARQUET"
                      + format      = "PARQUET"
                      + output_type = "CUSTOM"
                      + overwrite   = "OVERWRITE_REPORT"
                    }
                }
            }

          + refresh_cadence {
              + frequency = "SYNCHRONOUS"
            }
        }
    }

  # module.cost_analytics.aws_glue_crawler.cur will be created
  + resource "aws_glue_crawler" "cur" {
      + arn           = (known after apply)
      + configuration = jsonencode(
            {
              + Grouping = {
                  + TableGroupingPolicy = "CombineCompatibleSchemas"
                }
              + Version  = 1
            }
        )
      + database_name = "javabin_cur"
      + id            = (known after apply)
      + name          = "javabin-cur-crawler"
      + role          = (known after apply)
      + schedule      = "cron(0 6 * * ? *)"
      + tags_all      = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }

      + s3_target {
          + path = "s3://javabin-cur-553637109631/cur/"
        }

      + schema_change_policy {
          + delete_behavior = "DELETE_FROM_DATABASE"
          + update_behavior = "UPDATE_IN_DATABASE"
        }
    }

  # module.cost_analytics.aws_iam_role.cur_crawler will be created
  + resource "aws_iam_role" "cur_crawler" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "glue.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "javabin-cur-crawler"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + unique_id             = (known after apply)
    }

  # module.cost_analytics.aws_iam_role_policy.cur_crawler_s3 will be created
  + resource "aws_iam_role_policy" "cur_crawler_s3" {
      + id          = (known after apply)
      + name        = "javabin-cur-crawler-s3"
      + name_prefix = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = [
                          + "s3:GetObject",
                          + "s3:ListBucket",
                        ]
                      + Effect   = "Allow"
                      + Resource = [
                          + "arn:aws:s3:::javabin-cur-553637109631",
                          + "arn:aws:s3:::javabin-cur-553637109631/*",
                        ]
                      + Sid      = "ReadCURData"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + role        = (known after apply)
    }

  # module.cost_analytics.aws_iam_role_policy_attachment.cur_crawler_glue will be created
  + resource "aws_iam_role_policy_attachment" "cur_crawler_glue" {
      + id         = (known after apply)
      + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
      + role       = "javabin-cur-crawler"
    }

  # module.dns.aws_route53_record.java_no_a will be created
  + resource "aws_route53_record" "java_no_a" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "java.no"
      + records         = [
          + "16.16.253.0",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_dkim_k2 will be created
  + resource "aws_route53_record" "java_no_dkim_k2" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "k2._domainkey.java.no"
      + records         = [
          + "dkim2.mcsv.net.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_dkim_k3 will be created
  + resource "aws_route53_record" "java_no_dkim_k3" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "k3._domainkey.java.no"
      + records         = [
          + "dkim3.mcsv.net.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_dkim_s1 will be created
  + resource "aws_route53_record" "java_no_dkim_s1" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "s1._domainkey.java.no"
      + records         = [
          + "s1.domainkey.u8086282.wl033.sendgrid.net.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_dkim_s2 will be created
  + resource "aws_route53_record" "java_no_dkim_s2" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "s2._domainkey.java.no"
      + records         = [
          + "s2.domainkey.u8086282.wl033.sendgrid.net.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_dmarc will be created
  + resource "aws_route53_record" "java_no_dmarc" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_dmarc.java.no"
      + records         = [
          + "v=DMARC1; p=none;",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["j-team"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "j-team.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["javazone-2017"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javazone-2017.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["javazone-2018"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javazone-2018.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["javazone-2019"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javazone-2019.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["javazone-2020"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javazone-2020.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_github_pages["text-adventure"] will be created
  + resource "aws_route53_record" "java_no_github_pages" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "text-adventure.java.no"
      + records         = [
          + "javaBin.github.io.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_lister will be created
  + resource "aws_route53_record" "java_no_lister" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "lister.java.no"
      + records         = [
          + "ghs.googlehosted.com.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_mx will be created
  + resource "aws_route53_record" "java_no_mx" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "java.no"
      + records         = [
          + "1 ASPMX.L.GOOGLE.COM.",
          + "10 ALT3.ASPMX.L.GOOGLE.COM.",
          + "10 ALT4.ASPMX.L.GOOGLE.COM.",
          + "5 ALT1.ASPMX.L.GOOGLE.COM.",
          + "5 ALT2.ASPMX.L.GOOGLE.COM.",
        ]
      + ttl             = 3600
      + type            = "MX"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_sendgrid will be created
  + resource "aws_route53_record" "java_no_sendgrid" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "em8650.java.no"
      + records         = [
          + "u8086282.wl033.sendgrid.net.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_spf will be created
  + resource "aws_route53_record" "java_no_spf" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "java.no"
      + records         = [
          + "google-site-verification=AF0DKgeCRjhwASd4SPtC-wKS8bBFkpQJDReud486Vxw",
          + "google-site-verification=VrR4VP57L2aSHRB-jsbpMixnRSI98siDpimdHgpSKo8",
          + "v=spf1 mx ip4:212.71.237.26 include:_spf.google.com ~all",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_wildcard will be created
  + resource "aws_route53_record" "java_no_wildcard" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "*.java.no"
      + records         = [
          + "16.16.253.0",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.java_no_www4_verification will be created
  + resource "aws_route53_record" "java_no_www4_verification" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www4.java.no"
      + records         = [
          + "google-site-verification=GAxI89cdBzVJRVwvA-xLVj7UI5tqDMAHlZ7eomK1OIU",
        ]
      + ttl             = 300
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_com_a will be created
  + resource "aws_route53_record" "javabin_com_a" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javabin.com"
      + records         = [
          + "194.63.248.52",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_com_aaaa will be created
  + resource "aws_route53_record" "javabin_com_aaaa" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javabin.com"
      + records         = [
          + "2a01:5b40:0:248::52",
        ]
      + ttl             = 3600
      + type            = "AAAA"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_com_dmarc will be created
  + resource "aws_route53_record" "javabin_com_dmarc" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_dmarc.javabin.com"
      + records         = [
          + "v=DMARC1; p=none",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_com_www will be created
  + resource "aws_route53_record" "javabin_com_www" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www.javabin.com"
      + records         = [
          + "194.63.248.52",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_no_a will be created
  + resource "aws_route53_record" "javabin_no_a" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javabin.no"
      + records         = [
          + "194.63.248.52",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_no_aaaa will be created
  + resource "aws_route53_record" "javabin_no_aaaa" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javabin.no"
      + records         = [
          + "2a01:5b40:0:248::52",
        ]
      + ttl             = 3600
      + type            = "AAAA"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_no_dmarc will be created
  + resource "aws_route53_record" "javabin_no_dmarc" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_dmarc.javabin.no"
      + records         = [
          + "v=DMARC1; p=none",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_no_mx will be created
  + resource "aws_route53_record" "javabin_no_mx" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "javabin.no"
      + records         = [
          + "5 mail.java.no.",
        ]
      + ttl             = 300
      + type            = "MX"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.javabin_no_www will be created
  + resource "aws_route53_record" "javabin_no_www" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www.javabin.no"
      + records         = [
          + "194.63.248.52",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_a will be created
  + resource "aws_route53_record" "teknologihuset_no_a" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "teknologihuset.no"
      + records         = [
          + "13.53.169.151",
        ]
      + ttl             = 3600
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_autoconfig will be created
  + resource "aws_route53_record" "teknologihuset_no_autoconfig" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "autoconfig.teknologihuset.no"
      + records         = [
          + "autoconfig.domeneshop.no.",
        ]
      + ttl             = 3600
      + type            = "CNAME"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_autodiscover will be created
  + resource "aws_route53_record" "teknologihuset_no_autodiscover" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_autodiscover._tcp.teknologihuset.no"
      + records         = [
          + "0 0 443 autoconfig.domeneshop.no.",
        ]
      + ttl             = 3600
      + type            = "SRV"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_caldav_srv will be created
  + resource "aws_route53_record" "teknologihuset_no_caldav_srv" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_caldavs._tcp.teknologihuset.no"
      + records         = [
          + "0 0 443 caldav.domeneshop.no.",
        ]
      + ttl             = 3600
      + type            = "SRV"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_caldav_txt will be created
  + resource "aws_route53_record" "teknologihuset_no_caldav_txt" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_caldavs._tcp.teknologihuset.no"
      + records         = [
          + "path=/",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_carddav_srv will be created
  + resource "aws_route53_record" "teknologihuset_no_carddav_srv" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_carddavs._tcp.teknologihuset.no"
      + records         = [
          + "0 0 443 carddav.domeneshop.no.",
        ]
      + ttl             = 3600
      + type            = "SRV"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_carddav_txt will be created
  + resource "aws_route53_record" "teknologihuset_no_carddav_txt" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_carddavs._tcp.teknologihuset.no"
      + records         = [
          + "path=/",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_dmarc will be created
  + resource "aws_route53_record" "teknologihuset_no_dmarc" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_dmarc.teknologihuset.no"
      + records         = [
          + "v=DMARC1; p=quarantine; rua=mailto:dmarc@domeneshop.no",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_github_challenge will be created
  + resource "aws_route53_record" "teknologihuset_no_github_challenge" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "_github-challenge-teknologihuset.teknologihuset.no"
      + records         = [
          + "4cadd5f17c",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_mx will be created
  + resource "aws_route53_record" "teknologihuset_no_mx" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "teknologihuset.no"
      + records         = [
          + "10 ASPMX.L.GOOGLE.COM.",
          + "20 ALT1.ASPMX.L.GOOGLE.COM.",
          + "20 ALT2.ASPMX.L.GOOGLE.COM.",
          + "30 ASPMX2.GOOGLEMAIL.COM.",
          + "30 ASPMX3.GOOGLEMAIL.COM.",
        ]
      + ttl             = 3600
      + type            = "MX"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_spf will be created
  + resource "aws_route53_record" "teknologihuset_no_spf" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "teknologihuset.no"
      + records         = [
          + "v=spf1 include:_spf.domeneshop.no ~all",
        ]
      + ttl             = 3600
      + type            = "TXT"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_record.teknologihuset_no_www will be created
  + resource "aws_route53_record" "teknologihuset_no_www" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www.teknologihuset.no"
      + records         = [
          + "13.53.169.151",
        ]
      + ttl             = 300
      + type            = "A"
      + zone_id         = (known after apply)
    }

  # module.dns.aws_route53_zone.java_no will be created
  + resource "aws_route53_zone" "java_no" {
      + arn                 = (known after apply)
      + comment             = "Managed by Terraform"
      + force_destroy       = false
      + id                  = (known after apply)
      + name                = "java.no"
      + name_servers        = (known after apply)
      + primary_name_server = (known after apply)
      + tags                = {
          + "Name" = "java.no"
        }
      + tags_all            = {
          + "Name"        = "java.no"
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + zone_id             = (known after apply)
    }

  # module.dns.aws_route53_zone.javabin_com will be created
  + resource "aws_route53_zone" "javabin_com" {
      + arn                 = (known after apply)
      + comment             = "Managed by Terraform"
      + force_destroy       = false
      + id                  = (known after apply)
      + name                = "javabin.com"
      + name_servers        = (known after apply)
      + primary_name_server = (known after apply)
      + tags                = {
          + "Name" = "javabin.com"
        }
      + tags_all            = {
          + "Name"        = "javabin.com"
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + zone_id             = (known after apply)
    }

  # module.dns.aws_route53_zone.javabin_no will be created
  + resource "aws_route53_zone" "javabin_no" {
      + arn                 = (known after apply)
      + comment             = "Managed by Terraform"
      + force_destroy       = false
      + id                  = (known after apply)
      + name                = "javabin.no"
      + name_servers        = (known after apply)
      + primary_name_server = (known after apply)
      + tags                = {
          + "Name" = "javabin.no"
        }
      + tags_all            = {
          + "Name"        = "javabin.no"
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + zone_id             = (known after apply)
    }

  # module.dns.aws_route53_zone.teknologihuset_no will be created
  + resource "aws_route53_zone" "teknologihuset_no" {
      + arn                 = (known after apply)
      + comment             = "Managed by Terraform"
      + force_destroy       = false
      + id                  = (known after apply)
      + name                = "teknologihuset.no"
      + name_servers        = (known after apply)
      + primary_name_server = (known after apply)
      + tags                = {
          + "Name" = "teknologihuset.no"
        }
      + tags_all            = {
          + "Name"        = "teknologihuset.no"
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + zone_id             = (known after apply)
    }

Plan: 48 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🟢 LOW

Plan adds DNS zones and records for Javabin domains plus cost analytics infrastructure (Glue crawler and BCM data exports) with no destructive changes.

  • [routine] Creating 4 Route53 hosted zones (java.no, javabin.no, javabin.com, teknologihuset.no) with 40+ DNS records for email, verification, and GitHub Pages routing. Standard DNS management.
  • [routine] Adding AWS Glue crawler for CUR data analysis with daily schedule (6 AM UTC). Includes IAM role with S3 read permissions scoped to CUR bucket only.
  • 💰 [cost] New BCM Data Exports resource (aws_bcmdataexports_export) for daily CUR export to S3 in Parquet format. Minimal cost impact (~$0.01/GB for CUR data typically).
  • [routine] All DNS records point to external IPs (194.63.248.52, 13.53.169.151, Google/SendGrid services). No security group or IAM changes. No public access modifications.
  • [routine] No resources being destroyed or modified. All 48 changes are new resource creation with no force-replacement or data loss risk.

@Alexanderamiri Alexanderamiri merged commit 8f51bde into main Mar 26, 2026
3 checks passed
@Alexanderamiri Alexanderamiri deleted the feat/dns-migration branch March 26, 2026 22:58
Alexanderamiri added a commit that referenced this pull request May 9, 2026
## Summary
- Creates Route53 hosted zones for **java.no**, **javabin.no**,
**javabin.com**, and **teknologihuset.no**
- Replicates all existing DNS records from Domeneshop (exported via
their API)
- Registrar stays at Domeneshop — only DNS management moves to Route53
- No DNS cutover happens on merge — NS records at Domeneshop must be
updated manually per domain

## Records migrated
| Domain | Records | Notes |
|--------|---------|-------|
| java.no | 18 | Google Workspace MX, SPF, DMARC, DKIM
(Mailchimp+SendGrid), wildcard A, GitHub Pages |
| javabin.no | 5 | MX → mail.java.no, DMARC, A/AAAA defaults |
| javabin.com | 4 | DMARC, A/AAAA defaults |
| teknologihuset.no | 12 | Google MX, Domeneshop email autodiscovery,
CalDAV/CardDAV SRV, GitHub challenge |

## Cutover plan (post-merge)
1. **javabin.com** — simplest, cut over first as validation
2. **javabin.no** — simple, cut over second
3. **java.no** — main org domain, cut over third
4. **teknologihuset.no** — has Domeneshop email+hosting, cut over last

Each cutover = change NS records at Domeneshop to the Route53
nameservers from `terraform output`.

## Test plan
- [ ] CI plan shows 43 resources to add, 0 to change/destroy
- [ ] After apply, verify each zone's NS records via `aws route53
list-hosted-zones`
- [ ] Before each NS cutover, compare `dig @<r53-ns> domain` vs `dig
@ns1.hyp.net domain`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant