【最終回】静的Webサイトホスティング二重化してみる(その4)
静的Webサイトホスティングパターンの二重化、前回から半年近くが経過しました。色々と試行錯誤していてようやくCloudFormationとかDeploymentManagerとかTerraformもなれてきたので宿題だったIaC化にチャレンジしました。
前提条件としてDockerでAWS-CLIとかGoogleCloud-SDK丸ごと込々の環境を作成できていることが必要になります。とりあえずそちらはDockerでサクッと作りました。
ほんとはGitHubのところまでできたらよかったんですが、そこはスキルがなくてヨワヨワなのでここまでが限界でした。ひとまず静的Webサイトホスティングパターンはここまでで最後です。ようやく昨年からの懸案事項解決できました。
◆シリーズ
その2:GoocleCloudでLoadBaranser+GCS構成を作りDNSでフェイルオーバ
その3:S3とGCSにGitHubActions使ってコンテンツ同期
その4:TerraformでIaC化
◆設定作業編
1.Docker内初期設定
(1)AWS初期設定
#コンテナログイン $ docker exec -it terraform-dev /bin/bash #AWS認証情報 # aws configure AWS Access Key ID [None]: INPUTACCESSKEY AWS Secret Access Key [None]: INPUTSECRETACCESSKEY Default region name [None]: ap-northeast-1 Default output format [None]: json #環境変数設定 # cd ~/terraform/ # export TF_VAR_access_key="ACCESSKEY" # export TF_VAR_secret_key="SECRETACCESSKEY" # export TF_VAR_role_arn="ROLE/ARN"
(2)GCP認証情報
#初期設定 # gcloud init Welcome! This command will take you through the configuration of gcloud. ~中略~ You must log in to continue. Would you like to log in (Y/n)? Y ~中略~ Pick cloud project to use: ~中略~ Please enter numeric choice or text value (must exactly match list item):7 ~中略~ Do you want to configure a default Compute Region and Zone? (Y/n)? Y ~中略~ Please enter numeric choice or text value (must exactly match list item): 34 files and output formatting #プロジェクト設定 # export GCP_PROJECT_ID=$(gcloud config get-value project) # env | grep GCP_PROJECT_ID G #サービスアカウント作成 # gcloud iam service-accounts create terraform-serviceaccount --display-name "Account for Terraform" # gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} --member serviceAccount:terraform-serviceaccount@${GCP_PROJECT_ID}.iam.gserviceaccount.com --role roles/editor #クレデンシャルキー発行 # gcloud iam service-accounts keys create ~/.config/gcloud/sakey.json \ --iam-account hoge@project.iam.gserviceaccount.com #環境変数設定 # export GOOGLE_CLOUD_KEYFILE_JSON=~/.config/gcloud/sakey.json # export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
(3)Azure認証情報
#初期設定 クレデンシャル発行 # az login ~中略~ ・認証情報取得 # az account list --query "[].{name:name, subscriptionId:id, tenantId:tenantId}" ~中略~ # az account set --subscription="subscriptionID" # az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/subscriptionID" Creating 'Contributor' role assignment under scope '/subscriptions/subscriptionID' The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli { "appId": "appId", "displayName": "azure-XXXX", "name": "http://azure-XXXX", "password": "password", "tenant": "tenant" } ・環境変数設定 # export ARM_SUBSCRIPTION_ID=subscriptionId # export ARM_CLIENT_ID=appId # export ARM_CLIENT_SECRET=password # export ARM_TENANT_ID=tenant # export ARM_ENVIRONMENT=public
2.Terraform
2.1.ファイル群
(1)ルート配下
#main.tf ------------------- provider "aws" { region = "ap-northeast-1" } provider "aws" { alias = "use1" region = "us-east-1" } provider "google" { project = var.googleCloud.project credentials = file(var.googleCloud.credentials) } provider "google-beta" { project = var.googleCloud.project credentials = file(var.googleCloud.credentials) } module "zone_acm" { source = "./zone_acm" root_domain = var.root_domain providers = { aws = aws.use1 } } module "awsstatic" { source = "./awsstatic" root_domain = var.root_domain site_domain = var.site_domain bucket_name = var.name acm_cert = module.zone_acm.cert_arn dns_zone = module.zone_acm.domain_zone depends_on = [module.zone_acm] } module "gcpstatic" { source = "./gcpstatic" googleCloud = var.googleCloud googlebucket = var.googlebucket name = var.name root_domain = var.root_domain site_domain = var.site_domain dns_zone = module.zone_acm.domain_zone depends_on = [module.awsstatic] } module "failoverdns" { source = "./failoverdns" root_domain = var.root_domain site_domain = var.site_domain bucket_name = var.name dns_zone = module.zone_acm.domain_zone cloudfront_id = module.awsstatic.cloudfront_alias_id cloudfront_domain = module.awsstatic.cloudfront_alias_name gcp_ipv4_addr = module.gcpstatic.gcp_ipv4 gcp_ipv6_addr = module.gcpstatic.gcp_ipv6 depends_on = [module.gcpstatic] } #variables.tf ------------------- variable "site_domain" { default = "www.yourdomain" } variable "root_domain" { default = "yourdomain" } variable "name" { default = "yourdomain" } variable "googleCloud" { default = { project = "youre_GCP_projectId" credentials = "~/.config/gcloud/sakey.json" } } variable "googlebucket" { default = { bucket_name = "bucketname" region = "asia" storage_class = "MULTI_REGIONAL" log_region = "asia-northeast1" log_class = "NEARLINE" } } -------------------
(2)zoneacm配下
#main.tf ------------------- resource "aws_acm_certificate" "cert" { domain_name = var.root_domain validation_method = "DNS" subject_alternative_names = [ var.root_domain, "*.${var.root_domain}" ] tags = { ManagedBy = "terraform" Changed = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp()) } lifecycle { ignore_changes = [tags] } } resource "aws_route53_zone" "zone" { name = var.root_domain tags = { ManagedBy = "terraform" Changed = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp()) Name = var.root_domain } } resource "null_resource" "get_ns_records" { provisioner "local-exec" { command = "/bin/bash ./sh/get_ns_records.sh ${var.root_domain}" } } resource "aws_route53_record" "validations" { for_each = { for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = aws_route53_zone.zone.zone_id } resource "aws_acm_certificate_validation" "acm_validation" { certificate_arn = aws_acm_certificate.cert.arn validation_record_fqdns = [for record in aws_route53_record.validations : record.fqdn] } ------------------- #variables.tf ------------------- variable "root_domain" {} ------------------- #output.tf ------------------- output "domain_zone" { value = aws_route53_zone.zone.zone_id } output "cert_arn" { value = aws_acm_certificate.cert.arn } -------------------
(3)awsstatic配下
#s3.tf ------------------- data "aws_iam_policy_document" "cloudfront-logging-bucket" { statement { sid = "" effect = "Allow" principals { identifiers = ["*"] type = "*" } actions = [ "s3:ListBucket", "s3:PutObject", "s3:GetObject" ] resources = [ "arn:aws:s3:::${var.bucket_name}-cloudfront-logs", "arn:aws:s3:::${var.bucket_name}-cloudfront-logs/*" ] } } resource "aws_s3_bucket" "cloudfront-logging" { bucket = "${var.bucket_name}-cloudfront-logs" policy = data.aws_iam_policy_document.cloudfront-logging-bucket.json request_payer = "BucketOwner" } resource "aws_s3_bucket_public_access_block" "cloudfront-logging" { bucket = aws_s3_bucket.cloudfront-logging.bucket block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket" "website-logs" { bucket = "${var.bucket_name}-logs" acl = "log-delivery-write" tags = { ManagedBy = "terraform" Changed = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp()) } lifecycle { ignore_changes = [tags] } } resource "aws_s3_bucket" "static-site" { bucket = var.bucket_name acl = "private" policy = data.aws_iam_policy_document.static-site-bucket_policy.json versioning { enabled = true } logging { target_bucket = aws_s3_bucket.website-logs.bucket target_prefix = "${var.bucket_name}/" } lifecycle { ignore_changes = [tags] } } locals { s3-origin-id-static-site = "s3-origin-id-static-site" } data "aws_iam_policy_document" "static-site-bucket_policy" { statement { sid = "" effect = "Allow" principals { type = "AWS" identifiers = [aws_cloudfront_origin_access_identity.static-site-idntity.iam_arn] } actions = [ "s3:GetObject" ] resources = [ "arn:aws:s3:::${var.bucket_name}", "arn:aws:s3:::${var.bucket_name}/*" ] } } ------------------- #cloudfront.tf ------------------- resource "aws_cloudfront_distribution" "static-site-dst" { origin { domain_name = aws_s3_bucket.static-site.bucket_regional_domain_name origin_id = local.s3-origin-id-static-site s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.static-site-idntity.cloudfront_access_identity_path } } enabled = true is_ipv6_enabled = true comment = var.root_domain default_root_object = "index.html" logging_config { include_cookies = false bucket = aws_s3_bucket.cloudfront-logging.bucket_regional_domain_name prefix = "cloudfront" } aliases = [ var.root_domain, "www.${var.root_domain}", "main.${var.root_domain}" ] viewer_certificate { acm_certificate_arn = var.acm_cert minimum_protocol_version = "TLSv1.2_2019" ssl_support_method = "sni-only" cloudfront_default_certificate = false } custom_error_response { error_code = "404" response_code = "200" response_page_path = "/404.html" } default_cache_behavior { allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] target_origin_id = local.s3-origin-id-static-site compress = true viewer_protocol_policy = "redirect-to-https" forwarded_values { query_string = false cookies { forward = "none" } } } restrictions { geo_restriction { restriction_type = "none" } } } resource "aws_cloudfront_origin_access_identity" "static-site-idntity" { comment = "access-identity-static-site.s3.amazonaws.com" } resource "aws_route53_record" "root_domain" { zone_id = var.dns_zone name = var.root_domain type = "A" alias { name = aws_cloudfront_distribution.static-site-dst.domain_name zone_id = aws_cloudfront_distribution.static-site-dst.hosted_zone_id evaluate_target_health = false } } resource "aws_route53_record" "www_domain" { zone_id = var.dns_zone name = var.site_domain type = "A" alias { name = aws_cloudfront_distribution.static-site-dst.domain_name zone_id = aws_cloudfront_distribution.static-site-dst.hosted_zone_id evaluate_target_health = false } } ------------------- #variables.tf ------------------- variable "root_domain" {} variable "site_domain" {} variable "bucket_name" {} variable "acm_cert" {} variable "dns_zone" {} ------------------- #output.tf ------------------- output "cloudfront_alias_id" { value = aws_cloudfront_distribution.static-site-dst.hosted_zone_id } output "cloudfront_alias_name" { value = aws_cloudfront_distribution.static-site-dst.domain_name } output "cloudfront_dist_id" { value = aws_cloudfront_distribution.static-site-dst.id } -------------------
(4)gcpstatic配下
#cert.tf ------------------- resource "google_compute_managed_ssl_certificate" "naked_lb_cert" { name = "${var.name}-cert" managed { domains = [var.root_domain] } } resource "google_compute_managed_ssl_certificate" "www_lb_cert" { name = "www-${var.name}-cert" managed { domains = [var.site_domain] } } ------------------- #gcs.tf ------------------- resource "google_storage_bucket" "logs-bucket" { name = "${var.googlebucket.bucket_name}-logs" location = var.googlebucket.log_region storage_class = var.googlebucket.log_class force_destroy = true labels = { project = var.googleCloud.project } } resource "google_storage_bucket_acl" "logs-bucket_iam" { bucket = google_storage_bucket.logs-bucket.name role_entity = [ "WRITER:group-cloud-storage-analytics@google.com", ] default_acl = "projectprivate" } resource "google_storage_bucket" "bucket" { name = var.googlebucket.bucket_name location = var.googlebucket.region storage_class = var.googlebucket.storage_class force_destroy = true labels = { project = var.googleCloud.project } versioning { enabled = true } website { main_page_suffix = "index.html" not_found_page = "404.html" } logging { log_bucket = "${var.googlebucket.bucket_name}-logs" } uniform_bucket_level_access = true } resource "google_storage_bucket_iam_member" "staticweb_bucket_iam_member_object_viewer" { bucket = google_storage_bucket.bucket.name role = "roles/storage.legacyObjectReader" member = "allUsers" } ------------------- #loadbalancer.tf ------------------- resource "google_compute_backend_bucket" "bucket_backend" { name = "${var.name}-backend" bucket_name = google_storage_bucket.bucket.name } resource "google_compute_url_map" "urlmap" { name = "${var.name}-https-site" description = "URL map for ${var.name}" default_service = google_compute_backend_bucket.bucket_backend.self_link host_rule { hosts = ["*"] path_matcher = "all" } path_matcher { name = "all" default_service = google_compute_backend_bucket.bucket_backend.self_link path_rule { paths = ["/*"] service = google_compute_backend_bucket.bucket_backend.self_link } } } resource "google_compute_target_https_proxy" "https_proxy" { name = "${var.name}-proxy" url_map = google_compute_url_map.urlmap.self_link ssl_certificates = [ google_compute_managed_ssl_certificate.naked_lb_cert.id, google_compute_managed_ssl_certificate.www_lb_cert.id ] } resource "google_compute_global_forwarding_rule" "https_ipv4" { name = "${var.name}-v4-fwdrule" target = google_compute_target_https_proxy.https_proxy.id port_range = "443" ip_address = google_compute_global_address.lb_v4_address.address } resource "google_compute_global_forwarding_rule" "https_ipv6" { name = "${var.name}-v6-fwdrule" target = google_compute_target_https_proxy.https_proxy.id port_range = "443" ip_address = google_compute_global_address.lb_v6_address.address } resource "google_compute_url_map" "http_redirect" { name = "${var.name}-redirect" default_url_redirect { redirect_response_code = "MOVED_PERMANENTLY_DEFAULT" https_redirect = true strip_query = false } } resource "google_compute_target_http_proxy" "http_default" { name = "${var.name}-https-redirect-proxy" url_map = google_compute_url_map.http_redirect.self_link } resource "google_compute_global_forwarding_rule" "http_ipv4" { name = "${var.name}-http-v4-fwdrule" target = google_compute_target_http_proxy.http_default.self_link ip_address = google_compute_global_address.lb_v4_address.address port_range = "80" } resource "google_compute_global_forwarding_rule" "http_ipv6" { name = "${var.name}-http-v6-fwdrule" target = google_compute_target_http_proxy.http_default.self_link ip_address = google_compute_global_address.lb_v6_address.address port_range = "80" } resource "null_resource" "check_complete_lb" { provisioner "local-exec" { command = "/bin/bash ./sh/gcp_cert_check.sh ${var.root_domain}" } depends_on = [ aws_route53_record.gcp_root_domain, aws_route53_record.gcp_www_domain ] } ------------------- #gcpip.tf ------------------- resource "google_compute_global_address" "lb_v4_address" { name = "lb-v4-address" ip_version = "IPV4" } resource "google_compute_global_address" "lb_v6_address" { name = "lb-v6-address" ip_version = "IPV6" } resource "null_resource" "delete_aliasrecords" { provisioner "local-exec" { command = "/bin/bash ./sh/alias_arecord_delete.sh ${var.root_domain}" } depends_on = [ google_compute_global_address.lb_v4_address, google_compute_global_address.lb_v6_address ] } resource "aws_route53_record" "gcp_root_domain" { zone_id = var.dns_zone name = var.root_domain type = "A" ttl = "60" records = [google_compute_global_address.lb_v4_address.address] depends_on = [null_resource.delete_aliasrecords] } resource "aws_route53_record" "gcp_www_domain" { zone_id = var.dns_zone name = var.site_domain type = "A" ttl = "60" records = [google_compute_global_address.lb_v4_address.address] depends_on = [null_resource.delete_aliasrecords] } ------------------- #variables.tf ------------------- variable "site_domain" {} variable "root_domain" {} variable "dns_zone" {} variable "name" {} variable "googleCloud" {} variable "googlebucket" {} ------------------- #output.tf ------------------- output "gcp_ipv4" { value = google_compute_global_address.lb_v4_address.address } output "gcp_ipv6" { value = google_compute_global_address.lb_v6_address.address } -------------------
(5)failoverdns配下
#main.tf ------------------- resource "null_resource" "delete_a_records" { provisioner "local-exec" { command = "/bin/bash ./sh/ip_arecord_delete.sh ${var.root_domain}" } } resource "aws_route53_health_check" "cdn_healthcheck" { reference_name = "${var.bucket_name}_check" fqdn = var.cloudfront_domain failure_threshold = "3" type = "HTTPS" port = 443 request_interval = "30" tags = { Name = "${var.bucket_name}-web-check" } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "main_alias_ipv4" { zone_id = var.dns_zone name = "main.${var.root_domain}" type = "A" alias { name = var.cloudfront_domain zone_id = var.cloudfront_id evaluate_target_health = false } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "main_alias_ipv6" { zone_id = var.dns_zone name = "main.${var.root_domain}" type = "AAAA" alias { name = var.cloudfront_domain zone_id = var.cloudfront_id evaluate_target_health = false } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "root_domain" { zone_id = var.dns_zone name = var.root_domain type = "A" health_check_id = aws_route53_health_check.cdn_healthcheck.id set_identifier = "${var.bucket_name}-ipv4-main" failover_routing_policy { type = "PRIMARY" } alias { name = aws_route53_record.main_alias_ipv4.name zone_id = var.dns_zone evaluate_target_health = true } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "ipv6_root_domain" { zone_id = var.dns_zone name = var.root_domain type = "AAAA" health_check_id = aws_route53_health_check.cdn_healthcheck.id set_identifier = "${var.bucket_name}-ipv6-main" failover_routing_policy { type = "PRIMARY" } alias { name = aws_route53_record.main_alias_ipv6.name zone_id = var.dns_zone evaluate_target_health = true } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "www_domain" { zone_id = var.dns_zone name = var.site_domain type = "A" health_check_id = aws_route53_health_check.cdn_healthcheck.id set_identifier = "${var.bucket_name}-www-ipv4-main" failover_routing_policy { type = "PRIMARY" } alias { name = aws_route53_record.main_alias_ipv4.name zone_id = var.dns_zone evaluate_target_health = true } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "ipv6_www_domain" { zone_id = var.dns_zone name = var.site_domain type = "AAAA" health_check_id = aws_route53_health_check.cdn_healthcheck.id set_identifier = "${var.bucket_name}-www-ipv6-main" failover_routing_policy { type = "PRIMARY" } alias { name = aws_route53_record.main_alias_ipv6.name zone_id = var.dns_zone evaluate_target_health = true } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "gcp_root_domain" { zone_id = var.dns_zone name = var.root_domain type = "A" ttl = "60" records = [var.gcp_ipv4_addr] set_identifier = "${var.bucket_name}-ipv4-sub" failover_routing_policy { type = "SECONDARY" } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "gcp_ipv6_root_domain" { zone_id = var.dns_zone name = var.root_domain type = "AAAA" ttl = "60" records = [var.gcp_ipv6_addr] set_identifier = "${var.bucket_name}-ipv6-sub" failover_routing_policy { type = "SECONDARY" } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "gcp_www_domain" { zone_id = var.dns_zone name = var.site_domain type = "A" ttl = "60" records = [var.gcp_ipv4_addr] set_identifier = "${var.bucket_name}-www-ipv4-sub" failover_routing_policy { type = "SECONDARY" } depends_on = [null_resource.delete_a_records] } resource "aws_route53_record" "gcp_ipv6_www_domain" { zone_id = var.dns_zone name = var.site_domain type = "AAAA" ttl = "60" records = [var.gcp_ipv6_addr] set_identifier = "${var.bucket_name}-www-ipv6-sub" failover_routing_policy { type = "SECONDARY" } depends_on = [null_resource.delete_a_records] } ------------------- #variables.tf ------------------- variable "root_domain" {} variable "site_domain" {} variable "bucket_name" {} variable "dns_zone" {} variable "cloudfront_id" {} variable "cloudfront_domain" {} variable "gcp_ipv4_addr" {} variable "gcp_ipv6_addr" {} -------------------
(6)sh配下
#get_ns_records.sh ------------------- #!/bin/bash if [ $# != 1 ]; then echo 'Empty CertName! Please [./get_ns_records.sh yourdomain]' exit 1 fi domain=$1 route53result=$(aws route53 list-hosted-zones-by-name --dns-name ${domain} | jq -r ".HostedZones[0].Id") hz=$(echo ${route53result} | sed -e "s!/hostedzone/!!g") if [ "$hz" = "" ]; then echo 'Fail get HostedZoneId' exit 1 fi echo '*** Please Setting Your Domain Nameserve ***' route53records=$(aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/${hz}) array=$(echo ${route53records} | \ jq -c -r '.ResourceRecordSets[]| if .Type == "NS" then .ResourceRecords[].Value else empty end' ) for i in ${array[@]} do echo ${i} done ------------------- #gcp_cert_check.sh ------------------- #!/bin/bash SLEEP_TIME=30 STATUS_CODE="ACTIVE" if [ $# != 1 ]; then echo 'Empty CertName! Please [./gcp_cert_check.sh yourdomain]' exit 1 fi domain=$1 # Check Cert status certname=$(echo ${domain/./-}) echo "*** ssl cert status check ***" while true do echo "Check now please wait・・・" sleep ${SLEEP_TIME} naked_code=$(echo $(gcloud beta compute ssl-certificates describe ${certname}-cert \ --global \ --format="get(managed.domainStatus)") | cut -d= -f2) www_code=$(echo $(gcloud beta compute ssl-certificates describe www-${certname}-cert \ --global \ --format="get(managed.domainStatus)") | cut -d= -f2) if [ "$naked_code" = "$STATUS_CODE" ] && [ "$www_code" = "$STATUS_CODE" ]; then break fi done echo "*** Setting Complete ***" ------------------- #alias_arecord_delete.sh ------------------- #!/bin/bash SLEEP_TIME=10 BREAK_WORD="INSYNC" JSON_FILE=`mktemp` if [ $# != 1 ]; then echo 'Empty Domain! Please [./alias_arecord_delete.sh yourdomain]' exit 1 fi domainname=$1 # Get Hostedzoneid route53result=$(aws route53 list-hosted-zones-by-name --dns-name ${domainname}) hosted=$(echo ${route53result} | jq -r ".HostedZones[0].Id") hz=$(echo ${hosted} | sed -e "s!/hostedzone/!!g") # Route53 Make TargetRecords route53records=$(aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/${hz}) DELIMITA="," RECTYPES=("A") for (( i = 0; i < ${#RECTYPES[@]}; ++i )); do if [ $i -gt 0 ]; then BODY+="$DELIMITA " fi route53alias=$(echo ${route53records} | jq -c -r '.ResourceRecordSets[] | if .Type == "'${RECTYPES[$i]}'" then .AliasTarget else empty end') route53dnsname=$(echo ${route53records} | jq -c -r '.ResourceRecordSets[] | if .Type == "'${RECTYPES[$i]}'" then .Name else empty end') RECORD_TYPE=${RECTYPES[$i]} RESOURCE_VALUE=(`echo $route53alias`) DNS_NAME=(`echo $route53dnsname`) for (( j = 0; j < ${#RESOURCE_VALUE[@]}; ++j )); do if [ $j -gt 0 ]; then BODY+="$DELIMITA" fi BODY+=$(cat <$JSON_FILE # Deleting DNS Record set jobid=$(aws route53 change-resource-record-sets --hosted-zone-id ${hz} --change-batch file://$JSON_FILE) JOBID=$(echo ${jobid} | jq -r ".ChangeInfo.Id" | sed -e "s!/change/!!g") #Status check while true do sleep ${SLEEP_TIME} route53stat=$(aws route53 get-change --id /change/${JOBID}) stat=$(echo ${route53stat} | jq -r ".ChangeInfo.Status") if [ "$stat"=${BREAK_WORD} ]; then break fi done echo "*** Setting Complete ***" ------------------- #ip_arecord_delete.sh ------------------- #!/bin/bash SLEEP_TIME=10 BREAK_WORD="INSYNC" JSON_FILE=`mktemp` if [ $# != 1 ]; then echo 'Empty Domain! Please [./ip_arecord_delete.sh yourdomain]' exit 1 fi domainname=$1 # Get Hostedzoneid route53result=$(aws route53 list-hosted-zones-by-name --dns-name ${domainname}) hosted=$(echo ${route53result} | jq -r ".HostedZones[0].Id") hz=$(echo ${hosted} | sed -e "s!/hostedzone/!!g") # Route53 Make TargetRecords route53records=$(aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/${hz}) DELIMITA="," RECTYPES=("A") for (( i = 0; i < ${#RECTYPES[@]}; ++i )); do if [ $i -gt 0 ]; then BODY+="$DELIMITA " fi route53resourcevalue=$(echo ${route53records} | jq -c -r '.ResourceRecordSets[] | if .Type == "'${RECTYPES[$i]}'" then .ResourceRecords[].Value else empty end') route53dnsname=$(echo ${route53records} | jq -c -r '.ResourceRecordSets[] | if .Type == "'${RECTYPES[$i]}'" then .Name else empty end') RECORD_TYPE=${RECTYPES[$i]} route53ttl=$(echo ${route53records} | jq -c -r '.ResourceRecordSets[] | if .Type == "'${RECTYPES[$i]}'" then .TTL else empty end') RESOURCE_VALUE=(`echo $route53resourcevalue`) DNS_NAME=(`echo $route53dnsname`) TTL=(`echo $route53ttl`) for (( j = 0; j < ${#RESOURCE_VALUE[@]}; ++j )); do if [ $j -gt 0 ]; then BODY+="$DELIMITA" fi BODY+=$(cat <$JSON_FILE # Deleting DNS Record set jobid=$(aws route53 change-resource-record-sets --hosted-zone-id ${hz} --change-batch file://$JSON_FILE) JOBID=$(echo ${jobid} | jq -r ".ChangeInfo.Id" | sed -e "s!/change/!!g") #Status check while true do sleep ${SLEEP_TIME} route53stat=$(aws route53 get-change --id /change/${JOBID}) stat=$(echo ${route53stat} | jq -r ".ChangeInfo.Status") if [ "$stat"=${BREAK_WORD} ]; then break fi done echo "*** Setting Complete ***" -------------------
2.2.ディレクトリ構造
. |-- awsstatic | |-- cloudfront.tf | |-- output.tf | |-- s3.tf | `-- variables.tf |-- failoverdns | |-- main.tf | `-- variables.tf |-- gcpstatic | |-- cert.tf | |-- gcpip.tf | |-- gcs.tf | |-- loadbalancer.tf | |-- output.tf | `-- variables.tf |-- main.tf |-- sh | |-- alias_arecord_delete.sh | |-- gcp_cert_check.sh | |-- get_ns_records.sh | `-- ip_arecord_delete.sh |-- terraform.tfstate |-- terraform.tfstate.backup |-- variables.tf `-- zone_acm |-- main.tf |-- output.tf `-- variables.tf◆参考サイト
・terraform
https://dev.classmethod.jp/articles/terraform_module_coordination/
https://kazuhira-r.hatenablog.com/entry/2020/06/07/175759
https://medium.com/hashicorp-engineering/creating-module-dependencies-in-terraform-0-13-4322702dac4a
https://dev.classmethod.jp/articles/terraform013/
https://qiita.com/takkii1010/items/082c0854fd41bc0b26c3
・terraform特定リソース削除
https://www.devopsschool.com/blog/how-to-destroy-one-specific-resource-from-tf-file-in-terraform/
https://stackoverflow.com/questions/55265203/terraform-delete-all-resources-except-one
https://christina04.hatenablog.com/entry/2015/09/07/211925
https://kaoru2012.blogspot.com/2019/09/terraform_24.html
・terraformShell呼出し
https://dev.to/sagarjadhv23/executing-shell-script-in-terraform-via-null-resource-872
https://www.terraform.io/docs/language/resources/provisioners/local-exec.html
・S3+CloudFront+ACM
https://qiita.com/keitarounakano/items/11358d6814d7e9680054
https://qiita.com/neruneruo/items/cd550894aed76c11458c
https://github.com/cloudmaniac/terraform-aws-static-website/blob/master/main.tf
https://tech.lucheholdings.com/entry/2018/09/25/220855
https://qiita.com/matsuda-hiroki/items/25686218cafff6ed0989
https://katsuya-place.com/terraform-cloudfront/
・マルチリージョン
https://medium.com/@jyotti/terraform-multi-region-79a84ce1da0c
https://fusagiko.hatenablog.jp/entry/2019/12/01/advent_calendar
・Route53
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_health_check
https://blog.manabusakai.com/2020/04/change-the-routing-policy-in-terraform/
https://gside.org/blog/2016/10/16/
https://budougumi0617.github.io/2020/11/07/define_https_subdomain_by_terraform/
https://qiita.com/zembutsu/items/85ed9061288cae11bc21
https://dev.classmethod.jp/articles/route53-hostedzone-change-by-aws-cli/
https://qiita.com/iogi/items/676b8c5cbbb6ec874c37
・ACM
https://qiita.com/y-ohgi/items/b823b6bd078ce8e11546
https://github.com/hashicorp/terraform-provider-aws/issues/8531
https://zenn.dev/shonansurvivors/articles/5ee0e50ab2c915
・CloudFront CLI操作
https://www.semicolonworld.com/question/78733/aws-cli-disable-distribution
・GCP project関連
https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform?hl=ja
・LB
https://qiita.com/ktoshi/items/a43a54005aa73be1254d
https://qiita.com/ktoshi/items/1fd3c62b15993adce93c
https://cloud.google.com/blog/ja/products/serverless/serverless-load-balancing-terraform-hard-way
https://inside.pixiv.blog/2020/05/08/161041
・GCS
https://blog.grasys.io/post/okawa/tf-gcs-logging/
https://qiita.com/y_hideshi/items/5c8a5d5d4c7a30278378
https://kawabatas.hatenablog.com/entry/2018/08/12/114150
https://www.javaer101.com/en/article/28846055.html
・GCP IPアドレス発行
https://www.namakedame.work/terraform-gcp-static-ip/
https://github.com/terraform-google-modules/terraform-google-lb-http/issues/112
・Shell
http://psychedelicnekopunch.com/archives/2055
https://qiita.com/simarei/items/f4809314399562309e4b
https://genzouw.com/entry/2019/12/17/120057/1831/
https://qiita.com/ponsuke0531/items/a56126b8b218f74faa10
http://kodama.fubuki.info/wiki/wiki.cgi/bash/tips?lang=jp
http://amano41.hatenablog.jp/entry/bash-supports-addition-assignment-operator
https://www.appmeshworkshop.com/cleanup/route53/
https://takuya-1st.hatenablog.jp/entry/2017/03/22/144700
https://linuxfan.info/cat-string-in-shell
完成してきちんと動くのはイイのですがドメインのレジストラがTerraformからの制御に対応していないとかAPIが準備されていないとかなところが多いので、
Route53の登録後にNSレコードをレジストラ側に設定してやる必要があります。後はGoogleのLBのドメインの反映が驚くほど時間がかかるので、コマンド打ってから構築完了までに楽勝で15分ほどかかります。とりあえずこの構成だと非常にバカ高いというところがあって、どうにかして安価で抑える方法を考えたいのですがいい手立て思いつきません。ご教示いただけると幸いです。
それとRoute53のAレコード削除とかをShellスクリプトからキックしていたりするのであまりいいパターンではありません。よい子は真似をしないようにですね。
◆GitHub
いちおう上げています。(2022-07-03 Terraform v1.2.2で動作確認済)
https://github.com/Otazoman/terraform_dupulicate_staticsite/tree/main/aws_gcp ※2024年9月に少し修正して最新のterraform(1.9.5)で動作確認しました。GitHub参照してください。
コメント