Open11

書籍「実践Terraform AWSにおけるシステム設計とベストプラクティス」実践メモ

Yuma ItoYuma Ito

IAMユーザーの作成

アクセスキーを生成しようとしたら以下のようなレコメンデーションが表示された

推奨された代替案
ブラウザベースの CLI である AWS CloudShell を使用してコマンドを実行します。
AWS CLI V2 を使用し、IAM Identity Center のユーザーによる認証を有効にします。

アクセスキーの漏洩事故を防ぐため代替案が示されるのは素晴らしい

Yuma ItoYuma Ito

git-secrets

git-secrets という秘匿情報をコミットしないようにできるツールがある模様。
https://github.com/awslabs/git-secrets

以下のコマンドでインストール

brew install git-secrets

すべてのリポジトリでチェックするように設定

git secrets --register-aws --global
git secrets --install ~/.git-templates/git-secrets
git config --global init.templateDir ~/.git-templates/git-secrets
Yuma ItoYuma Ito

Terraformのバージョン

terraform -v
Terraform v1.9.8
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v5.77.0
Yuma ItoYuma Ito

2.2 リソースの更新

2.2.2 リソースの再作成

本ではApacheをインストールするためにuser_dataを追加するとEC2インスタンスが削除&作成されると書かれていた。
しかし、仕様変更があったのか既存のリソースが更新されるだけだった。

terraform apply
aws_instance.example: Refreshing state... [id=i-03e31efe04166ffaf]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
        id                                   = "i-03e31efe04166ffaf"
        tags                                 = {
            "Name" = "example"
        }
      + user_data                            = "5f11f9fb893f5c269243dd3d8efe2bc57983f538"
        # (39 unchanged attributes hidden)

        # (8 unchanged blocks hidden)
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.example: Modifying... [id=i-03e31efe04166ffaf]
aws_instance.example: Still modifying... [id=i-03e31efe04166ffaf, 10s elapsed]
aws_instance.example: Still modifying... [id=i-03e31efe04166ffaf, 20s elapsed]
aws_instance.example: Still modifying... [id=i-03e31efe04166ffaf, 30s elapsed]
aws_instance.example: Still modifying... [id=i-03e31efe04166ffaf, 40s elapsed]
aws_instance.example: Modifications complete after 43s [id=i-03e31efe04166ffaf]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Yuma ItoYuma Ito

3.6 参照

以下のようなコードでセキュリティグループの作成をしていた。

resource "aws_security_group" "example_ec2" {
  name = "example-ec2"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

しかし最新のproviderではaws_security_groupのリソース内にingressegressを設定する方法は非推奨になっていた。

You should not use the aws_security_group resource with in-line rules (using the ingress and egress arguments of aws_security_group) in conjunction with the aws_vpc_security_group_egress_rule and aws_vpc_security_group_ingress_rule resources or the aws_security_group_rule resource.
https://registry.terraform.io/providers/hashicorp/aws/5.77.0/docs/resources/security_group

代わりにaws_vpc_security_group_ingress_ruleaws_vpc_security_group_egress_ruleリソースを使うように書かれていたので、それに従った。

resource "aws_security_group" "example_ec2" {
  name = "example_ec2"
}

resource "aws_vpc_security_group_ingress_rule" "example_ec2" {
  security_group_id = aws_security_group.example_ec2.id
  from_port         = 80
  to_port           = 80
  ip_protocol       = "tcp"
  cidr_ipv4         = "0.0.0.0/0"
}

resource "aws_vpc_security_group_egress_rule" "example_ec2" {
  security_group_id = aws_security_group.example_ec2.id
  from_port         = 0
  to_port           = 0
  ip_protocol       = "-1"
  cidr_ipv4         = "0.0.0.0/0"
}
Yuma ItoYuma Ito

しかし、このコードでterraform applyをするとエラーになってしまった。

terraform apply
aws_security_group.example_ec2: Refreshing state... [id=sg-0351ca048ea26443f]
aws_instance.example: Refreshing state... [id=i-00b940f0a5ea6a4f3]

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:

  # aws_vpc_security_group_egress_rule.example_ec2 will be created
  + resource "aws_vpc_security_group_egress_rule" "example_ec2" {
      + arn                    = (known after apply)
      + cidr_ipv4              = "0.0.0.0/0"
      + from_port              = 0
      + id                     = (known after apply)
      + ip_protocol            = "-1"
      + security_group_id      = "sg-0351ca048ea26443f"
      + security_group_rule_id = (known after apply)
      + tags_all               = {}
      + to_port                = 0
    }

  # aws_vpc_security_group_ingress_rule.example_ec2 will be created
  + resource "aws_vpc_security_group_ingress_rule" "example_ec2" {
      + arn                    = (known after apply)
      + cidr_ipv4              = "0.0.0.0/0"
      + from_port              = 80
      + id                     = (known after apply)
      + ip_protocol            = "tcp"
      + security_group_id      = "sg-0351ca048ea26443f"
      + security_group_rule_id = (known after apply)
      + tags_all               = {}
      + to_port                = 80
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc_security_group_ingress_rule.example_ec2: Creating...
aws_vpc_security_group_egress_rule.example_ec2: Creating...
╷
│ Error: creating VPC Security Group Rule
│ 
│   with aws_vpc_security_group_ingress_rule.example_ec2,
│   on main.tf line 28, in resource "aws_vpc_security_group_ingress_rule" "example_ec2":
│   28: resource "aws_vpc_security_group_ingress_rule" "example_ec2" {
│ 
│ operation error EC2: AuthorizeSecurityGroupIngress, https response error StatusCode: 400, RequestID: 876657b8-e486-4779-8ed4-ebb8637b60a5, api
│ error InvalidPermission.Duplicate: the specified rule "peer: 0.0.0.0/0, TCP, from port: 80, to port: 80, ALLOW" already exists
╵
╷
│ Error: creating VPC Security Group Rule
│ 
│   with aws_vpc_security_group_egress_rule.example_ec2,
│   on main.tf line 37, in resource "aws_vpc_security_group_egress_rule" "example_ec2":
│   37: resource "aws_vpc_security_group_egress_rule" "example_ec2" {
│ 
│ operation error EC2: AuthorizeSecurityGroupEgress, https response error StatusCode: 400, RequestID: 2505842c-06f8-48bf-a657-1aa45db5edb3, api
│ error InvalidPermission.Duplicate: the specified rule "peer: 0.0.0.0/0, ALL, ALLOW" already exists
╵
Yuma ItoYuma Ito

このエラーはセキュリティグループのルールが重複していることを示している。
すでにインバウンドルールとアウトバウンドルールが作られていることがいけないので、一度AWSコンソールからルールを削除する。
(この方法が正しいのかは不明・・・。)

Yuma ItoYuma Ito

第6章 ストレージ

いくつか非推奨の設定があったので、下記のようにコードを書き換えた。

バージョニングの設定

resource "aws_s3_bucket_versioning" "private" {
  bucket = aws_s3_bucket.private.id
  versioning_configuration {
    status = "Enabled"
  }
}

暗号化の設定

resource "aws_s3_bucket_server_side_encryption_configuration" "private" {
  bucket = aws_s3_bucket.private.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

ACLの設定

resource "aws_s3_bucket_acl" "public" {
  bucket = aws_s3_bucket.public.id

  acl = "public-read"
}

CORSの設定

resource "aws_s3_bucket_cors_configuration" "public" {
  bucket = aws_s3_bucket.public.id

  cors_rule {
    allowed_origins = ["https://example.com"]
    allowed_methods = ["GET"]
    allowed_headers = ["*"]
    max_age_seconds = 3000
  }
}
Yuma ItoYuma Ito

Applyをしたところ、ACLの作成でエラー

terraform applyをしたら以下のようなエラーが発生した。

Error: creating S3 Bucket (public-yuma-ito-bd-pragmatic-terraform) ACL: operation error S3: PutBucketAcl, https response error StatusCode: 403, RequestID: xxxxx, HostID: xxxxx, api error AccessDenied: User: arn:aws:iam::xxxxxxx is not authorized to perform: s3:PutBucketAcl on resource: "arn:aws:s3:::xxxx" because public access control lists (ACLs) are blocked by the BlockPublicAcls block public access setting.

│ with aws_s3_bucket_acl.public,
│ on s3_public.tf line 7, in resource "aws_s3_bucket_acl" "public":
│ 7: resource "aws_s3_bucket_acl" "public" {

パブリックACLがブロックされていてエラーになっている模様。
しかし、下記のようにblock_public_aclsfalseに設定しているはずだが?

resource "aws_s3_bucket_public_access_block" "public" {
  bucket = aws_s3_bucket.public.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

この設定が効かなかった理由は、aws_s3_bucket_public_access_blockのリソースよりも先にaws_s3_bucket_aclを作成しようとしたからと思われる。

また、調べてみると、2023年の4月から新しく作られるS3バケットは自動的にS3パブリックアクセスブロックが有効化、アクセスコントロールリストが無効化されたとのこと。
https://aws.amazon.com/jp/about-aws/whats-new/2022/12/amazon-s3-automatically-enable-block-public-access-disable-access-control-lists-buckets-april-2023/

今はACLの設定は推奨されていない[1]ため、バケットポリシーによって制御することにした。

後でポリシーはいじる必要があるかもしれないが、とりあえず以下のように設定した。

# パブリックアクセスを許可するIAMポリシー
data "aws_iam_policy_document" "public" {
  statement {
    effect  = "Allow"
    actions = ["s3:GetObject"]
    resources = [
      "${aws_s3_bucket.public.arn}/*",
    ]

    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
  }
}

resource "aws_s3_bucket_policy" "public" {
  bucket = aws_s3_bucket.public.id
  policy = data.aws_iam_policy_document.public.json

  # デフォルトではパブリックアクセスブロックが有効化されているので、先に無効化する必要がある
  depends_on = [aws_s3_bucket_public_access_block.public]
}

depends_onによって、先にパブリックアクセスブロックを無効化してからバケットポリシーを適用するようにしているのがポイント。

参考:

脚注
  1. AWSのドキュメントには The majority of modern use cases in Amazon S3 do not require the use of ACLs. と記載されている。 ↩︎