TerraformでCloudFrontを構築する基礎知識
CloudFrontとTerraformの関係性を理解しよう
Amazon CloudFrontは、AWSが提供するCDN(Content Delivery Network)サービスです。TerraformはこのCloudFrontの構成をコードとして管理できる強力なツールです。
CloudFrontの主要コンポーネントとTerraformリソースの対応:
| CloudFrontコンポーネント | Terraformリソース | 説明 |
|---|---|---|
| Distribution | aws_cloudfront_distribution | CDNの基本設定を定義 |
| Origin | origin ブロック | コンテンツの配信元を設定 |
| Behavior | ordered_cache_behavior ブロック | URLパスごとの振る舞いを定義 |
| Function | aws_cloudfront_function | エッジでの軽量な処理を実装 |
Terraform管理のメリット3つと注意点2つ
メリット1:インフラのコード化(IaC)
- 設定の一元管理が可能
- バージョン管理システムとの連携
- コードレビューによる品質担保
# CloudFront Distributionの基本的な定義例
resource "aws_cloudfront_distribution" "example" {
origin {
domain_name = aws_s3_bucket.example.bucket_regional_domain_name
origin_id = "S3-${aws_s3_bucket.example.id}"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.example.cloudfront_access_identity_path
}
}
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
# 他の設定...
}
メリット2:環境の複製が容易
- 開発環境と本番環境の一貫性確保
- マルチリージョン展開の効率化
- ディザスタリカバリ対策の実装
メリット3:自動化との親和性
- CI/CDパイプラインとの統合
- 自動テストの実装
- デプロイの標準化
注意点1:状態管理の重要性
- tfstateファイルの適切な管理が必須
- リモートステート(S3+DynamoDB)の利用推奨
- 状態ロックによる同時実行制御
# バックエンド設定例
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "cloudfront/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
注意点2:無効化(Invalidation)の考慮
- キャッシュ無効化の適切な実装
- コスト管理の必要性
- デプロイ戦略との整合性
このように、TerraformでCloudFrontを管理することで、インフラストラクチャの管理を効率化できます。ただし、適切な状態管理と運用戦略の検討が重要です。
Terraformによるクラウドフロント構築手順
基本的なディストリビューション設定の書き方
CloudFrontディストリビューションの基本設定には、以下の要素が含まれます:
resource "aws_cloudfront_distribution" "main" {
# 基本設定
enabled = true
is_ipv6_enabled = true
comment = "My CloudFront Distribution"
default_root_object = "index.html"
# 価格クラスの選択(コスト最適化)
price_class = "PriceClass_200" # アジア・ヨーロッパ・北米をカバー
# アクセス制限
restrictions {
geo_restriction {
restriction_type = "whitelist"
locations = ["JP", "US"] # 日本とアメリカのみアクセス可能
}
}
# ビューワー証明書の設定
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cert.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}
オリジンの設定方法と最適な設定値
オリジンタイプごとの最適な設定:
1. S3バケットをオリジンとする場合
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = aws_s3_bucket.website.bucket_regional_domain_name
origin_id = "S3-${aws_s3_bucket.website.id}"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
# オリジンシールドの設定(オプション)
origin_shield {
enabled = true
origin_shield_region = "ap-northeast-1"
}
}
}
2. ALBをオリジンとする場合
resource "aws_cloudfront_distribution" "alb_distribution" {
origin {
domain_name = aws_lb.example.dns_name
origin_id = "ALB-${aws_lb.example.name}"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
custom_header {
name = "X-Origin-Verify"
value = var.origin_custom_header # セキュリティ強化
}
}
}
キャッシュ動作のカスタマイズ方法
キャッシュ設定は、パフォーマンスとコストに直接影響を与えます:
resource "aws_cloudfront_distribution" "web_app" {
# デフォルトのキャッシュ動作
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.origin_id
# キャッシュポリシーの設定
cache_policy_id = aws_cloudfront_cache_policy.example.id
# オリジンリクエストポリシー
origin_request_policy_id = aws_cloudfront_origin_request_policy.example.id
# レスポンスヘッダーポリシー
response_headers_policy_id = aws_cloudfront_response_headers_policy.example.id
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
# パスパターン別のキャッシュ動作
ordered_cache_behavior {
path_pattern = "/api/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.api_origin_id
forwarded_values {
query_string = true
headers = ["Authorization"]
cookies {
forward = "whitelist"
whitelisted_names = ["session-id"]
}
}
viewer_protocol_policy = "https-only"
min_ttl = 0
default_ttl = 0 # APIはキャッシュしない
max_ttl = 0
}
}
効果的なキャッシュ設定のポイント:
- 静的コンテンツは長めのTTL設定
- 動的コンテンツは適切なヘッダー転送設定
- セキュリティ要件に応じたCookieの管理
- キャッシュキーの最適化によるヒット率向上
実践的なTerraform設定例と解説
S3バケットをオリジンにした静的Webサイトの構築
# S3バケットの作成
resource "aws_s3_bucket" "website" {
bucket = "example-static-website"
}
resource "aws_s3_bucket_public_access_block" "website" {
bucket = aws_s3_bucket.website.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# CloudFront OAIの作成
resource "aws_cloudfront_origin_access_identity" "oai" {
comment = "OAI for ${aws_s3_bucket.website.bucket}"
}
# S3バケットポリシー
resource "aws_s3_bucket_policy" "website" {
bucket = aws_s3_bucket.website.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCloudFrontOAI"
Effect = "Allow"
Principal = {
AWS = aws_cloudfront_origin_access_identity.oai.iam_arn
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.website.arn}/*"
}
]
})
}
# CloudFront Distribution
resource "aws_cloudfront_distribution" "website" {
origin {
domain_name = aws_s3_bucket.website.bucket_regional_domain_name
origin_id = "S3-${aws_s3_bucket.website.bucket}"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
}
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html" # SPAのルーティング対応
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-${aws_s3_bucket.website.bucket}"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
compress = true
}
price_class = "PriceClass_200"
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
ALBをオリジンにしたアプリケーションの配信
# セキュリティグループの設定
resource "aws_security_group" "alb" {
name = "allow-cloudfront-only"
description = "Allow inbound traffic from CloudFront only"
vpc_id = var.vpc_id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = data.aws_ip_ranges.cloudfront.cidr_blocks
}
}
# ALBの設定
resource "aws_lb" "app" {
name = "app-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids
}
# CloudFront Distribution
resource "aws_cloudfront_distribution" "app" {
origin {
domain_name = aws_lb.app.dns_name
origin_id = "ALB-${aws_lb.app.name}"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
custom_header {
name = "X-Custom-Header"
value = random_password.origin_secret.result
}
}
enabled = true
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "ALB-${aws_lb.app.name}"
forwarded_values {
query_string = true
headers = ["Host", "Authorization"]
cookies {
forward = "all"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 0
max_ttl = 0
}
}
WAFとの連携でセキュリティを強化
# WAF IPレートリミッティングルール
resource "aws_wafv2_web_acl" "cloudfront" {
name = "cloudfront-waf"
description = "WAF rules for CloudFront"
scope = "CLOUDFRONT"
default_action {
allow {}
}
rule {
name = "RateLimit"
priority = 1
override_action {
none {}
}
statement {
rate_based_statement {
limit = 2000
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimitMetric"
sampled_requests_enabled = true
}
}
rule {
name = "BlockBadBots"
priority = 2
override_action {
none {}
}
statement {
byte_match_statement {
field_to_match {
single_header {
name = "user-agent"
}
}
positional_constraint = "CONTAINS"
search_string = "BadBot"
text_transformation {
priority = 1
type = "NONE"
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "BlockBadBotsMetric"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "CloudFrontWAFMetric"
sampled_requests_enabled = true
}
}
# CloudFrontとWAFの連携
resource "aws_cloudfront_distribution" "protected" {
# ... 他の設定 ...
web_acl_id = aws_wafv2_web_acl.cloudfront.id
# ... 他の設定 ...
}
これらの設定例は、実際の本番環境で使用できる実践的なものです。必要に応じてカスタマイズして使用してください。
CloudFrontの運用管理をTerraformで効率化
証明書の自動更新の実装方法
ACM証明書の自動更新をTerraformで実装する主なポイント:
# プロバイダー設定
provider "aws" {
alias = "virginia"
region = "us-east-1" # CloudFront用証明書はバージニアリージョンが必須
}
# ACM証明書の作成
resource "aws_acm_certificate" "main" {
provider = aws.virginia
domain_name = "example.com"
validation_method = "DNS"
subject_alternative_names = ["*.example.com"]
lifecycle {
create_before_destroy = true # 証明書の自動更新を有効化
}
tags = {
Name = "cloudfront-cert"
}
}
# Route 53でのDNS検証レコード
resource "aws_route53_record" "cert_validation" {
provider = aws.virginia
for_each = {
for dvo in aws_acm_certificate.main.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 = data.aws_route53_zone.main.zone_id
}
# 証明書の検証完了を待機
resource "aws_acm_certificate_validation" "cert" {
provider = aws.virginia
certificate_arn = aws_acm_certificate.main.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
# CloudFrontに証明書を関連付け
resource "aws_cloudfront_distribution" "main" {
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.main.arn
minimum_protocol_version = "TLSv1.2_2021"
ssl_support_method = "sni-only"
}
# その他の設定...
}
GitHubActionsでの自動デプロイ設定
name: 'Terraform CloudFront Deploy'
on:
push:
branches: [ main ]
paths:
- 'terraform/**'
pull_request:
branches: [ main ]
paths:
- 'terraform/**'
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
env:
AWS_DEFAULT_REGION: ap-northeast-1
TF_WORKSPACE: production
defaults:
run:
working-directory: ./terraform
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Init
run: |
terraform init \
-backend-config="bucket=${{ secrets.TF_STATE_BUCKET }}" \
-backend-config="key=cloudfront/terraform.tfstate"
- name: Terraform Plan
if: github.event_name == 'pull_request'
run: terraform plan -no-color
continue-on-error: true
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve
- name: Invalidate CloudFront
if: success() && github.event_name == 'push'
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*"
本番環境と開発環境の設定分離
設定分離のベストプラクティス:
# modules/cloudfront/main.tf
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = var.project_name
}
}
module "cloudfront" {
source = "./modules/cloudfront"
environment = terraform.workspace
distribution_config = {
price_class = local.env_configs[terraform.workspace].price_class
waf_enabled = local.env_configs[terraform.workspace].waf_enabled
logging_enabled = local.env_configs[terraform.workspace].logging_enabled
}
origin_config = {
domain_name = local.env_configs[terraform.workspace].origin_domain
custom_headers = local.env_configs[terraform.workspace].origin_headers
}
cache_behavior = {
min_ttl = local.env_configs[terraform.workspace].cache_ttl.min
default_ttl = local.env_configs[terraform.workspace].cache_ttl.default
max_ttl = local.env_configs[terraform.workspace].cache_ttl.max
}
tags = local.common_tags
}
# environments/prod/main.tf
locals {
env_configs = {
prod = {
price_class = "PriceClass_200"
waf_enabled = true
logging_enabled = true
origin_domain = "api.example.com"
origin_headers = {
"X-Environment" = "production"
}
cache_ttl = {
min = 0
default = 3600
max = 86400
}
}
dev = {
price_class = "PriceClass_100"
waf_enabled = false
logging_enabled = true
origin_domain = "dev-api.example.com"
origin_headers = {
"X-Environment" = "development"
}
cache_ttl = {
min = 0
default = 0
max = 0
}
}
}
}
# ワークスペース別の状態管理
terraform {
backend "s3" {
bucket = "terraform-state"
key = "cloudfront/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
主な利点:
- 証明書の自動更新による可用性確保
- GitHubActionsによる安全なCI/CD
- 環境ごとの設定管理の一元化
- インフラのバージョン管理とロールバック機能
- コスト最適化のための環境別設定
トラブルシューティングと解決策20選
デプロイ時によくあるエラーと対処法
1. 証明書関連のエラー
# エラー: Error creating CloudFront Distribution: InvalidViewerCertificate
# 原因: ACM証明書がus-east-1リージョンにない
provider "aws" {
alias = "virginia"
region = "us-east-1"
}
resource "aws_acm_certificate" "cert" {
provider = aws.virginia # 必ずバージニアリージョンを指定
# ...
}
2. OAIの権限エラー
# S3バケットポリシーの修正
resource "aws_s3_bucket_policy" "website" {
bucket = aws_s3_bucket.website.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCloudFrontOAI"
Effect = "Allow"
Principal = {
AWS = aws_cloudfront_origin_access_identity.oai.iam_arn
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.website.arn}/*"
}
]
})
}
3. tfstateの競合
# 状態ファイルのロック設定
terraform {
backend "s3" {
bucket = "terraform-state"
key = "cloudfront/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "terraform-locks" # 必ずロックテーブルを指定
encrypt = true
}
}
4. 無効化エラー
# 無効化の適切な実装
resource "null_resource" "invalidation" {
triggers = {
distribution_id = aws_cloudfront_distribution.main.id
}
provisioner "local-exec" {
command = <<EOF
aws cloudfront create-invalidation \
--distribution-id ${aws_cloudfront_distribution.main.id} \
--paths "/*"
EOF
}
}
パフォーマンス最適化のためのチェックポイント
1. オリジンシールドの設定
resource "aws_cloudfront_distribution" "optimized" {
origin {
domain_name = aws_s3_bucket.origin.bucket_regional_domain_name
origin_id = local.origin_id
origin_shield {
enabled = true
origin_shield_region = "ap-northeast-1" # 最も近いリージョン
}
}
}
2. 圧縮設定の最適化
resource "aws_cloudfront_distribution" "optimized" {
default_cache_behavior {
compress = true
forwarded_values {
query_string = false
headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
}
}
}
3. キャッシュ設定の最適化
resource "aws_cloudfront_cache_policy" "optimized" {
name = "optimized-caching"
min_ttl = 1
default_ttl = 86400 # 24時間
max_ttl = 31536000 # 1年
parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "whitelist"
headers {
items = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
}
}
query_strings_config {
query_string_behavior = "none"
}
}
}
コスト最適化のためのベストプラクティス
1. 価格クラスの最適化
resource "aws_cloudfront_distribution" "cost_optimized" {
price_class = "PriceClass_200" # アジア・ヨーロッパ・北米のみ
}
2. キャッシュヒット率の監視
resource "aws_cloudwatch_metric_alarm" "cache_hit" {
alarm_name = "cloudfront-cache-hit-rate"
comparison_operator = "LessThanThreshold"
evaluation_periods = "2"
metric_name = "CacheHitRate"
namespace = "AWS/CloudFront"
period = "300"
statistic = "Average"
threshold = "90"
alarm_description = "CloudFrontのキャッシュヒット率が90%を下回っています"
dimensions = {
DistributionId = aws_cloudfront_distribution.main.id
}
}
3. 不要なリージョンのブロック
resource "aws_cloudfront_distribution" "cost_optimized" {
restrictions {
geo_restriction {
restriction_type = "whitelist"
locations = ["JP", "US", "CA"] # 必要なリージョンのみ許可
}
}
}
トラブルシューティングのためのベストプラクティス
- デバッグ用ヘッダーの設定
resource "aws_cloudfront_distribution" "debug" {
default_cache_behavior {
response_headers_policy_id = aws_cloudfront_response_headers_policy.debug.id
}
}
resource "aws_cloudfront_response_headers_policy" "debug" {
name = "debug-headers"
custom_headers_config {
items {
header = "X-Cache-Debug"
override = true
value = "true"
}
items {
header = "X-Edge-Location"
override = true
value = "true"
}
}
}
- エラーページのカスタマイズ
resource "aws_cloudfront_distribution" "debug" {
custom_error_response {
error_code = 403
response_code = 200
response_page_path = "/error/403.html"
error_caching_min_ttl = 300
}
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/error/404.html"
error_caching_min_ttl = 300
}
}
- ログ設定の最適化
resource "aws_cloudfront_distribution" "logging" {
logging_config {
include_cookies = true
bucket = "${aws_s3_bucket.logs.bucket_domain_name}"
prefix = "cloudfront/"
}
}
resource "aws_s3_bucket" "logs" {
bucket = "cloudfront-logs-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_lifecycle_rule" "logs" {
bucket = aws_s3_bucket.logs.id
expiration {
days = 90 # 90日後に自動削除
}
}
これらの設定とベストプラクティスを実装することで、多くの一般的な問題を防ぎ、効率的な運用が可能になります。