あめがえるのITブログ

頑張りすぎない。ほどほどに頑張るブログ。

AWS RDS Proxy(AuroraMySQL)+SecretManager+VPCEndpoint構成を作成してみた

前にRDS(MySQL)をやったのでAuroraもやってみた。
違いはDBのClusterがあるかないかでそれによりRDSProxyの設定項目が少し変わる程度。
amegaeru.hatenablog.jp

構成




実践!

1.環境構築
1-1.terraformコードを作成

# vi main.tf
provider "aws" {
  region = "ap-northeast-1"
}

variable "env" {
  default = {
    env_name = "test"
    vpc_cidr = "10.0.0.0/16"
    sb_az1a = "ap-northeast-1a"
    sb_az1a_cidr = "10.0.1.0/24"
    sb_az1c = "ap-northeast-1c"
    sb_az1c_cidr = "10.0.2.0/24"
    sb_az1a_p = "ap-northeast-1a"
    sb_az1a_cidr_p = "10.0.3.0/24"
    sb_az1c_p = "ap-northeast-1c"
    sb_az1c_cidr_p = "10.0.4.0/24"
  }
}

### VPC
resource "aws_vpc" "vpc" {
    cidr_block = "${var.env.vpc_cidr}"
    enable_dns_support   = true
    enable_dns_hostnames = true
    tags = {
        Name = "${var.env.env_name}_vpc"
    }
}

### Subnet
resource "aws_subnet" "public_1a" {
  vpc_id = "${aws_vpc.vpc.id}"
  availability_zone = "${var.env.sb_az1a}"
  cidr_block        = "${var.env.sb_az1a_cidr}"
  tags = {
    Name = "${var.env.env_name}_public_1a"
  }
}

resource "aws_subnet" "public_1c" {
  vpc_id = "${aws_vpc.vpc.id}"
  availability_zone = "${var.env.sb_az1c}"
  cidr_block        = "${var.env.sb_az1c_cidr}"
  tags = {
    Name = "${var.env.env_name}_public_1c"
  }
}

resource "aws_subnet" "private_1a" {
  vpc_id = "${aws_vpc.vpc.id}"
  availability_zone = "${var.env.sb_az1a_p}"
  cidr_block        = "${var.env.sb_az1a_cidr_p}"
  tags = {
    Name = "${var.env.env_name}_private_1a"
  }
}

resource "aws_subnet" "private_1c" {
  vpc_id = "${aws_vpc.vpc.id}"
  availability_zone = "${var.env.sb_az1c_p}"
  cidr_block        = "${var.env.sb_az1c_cidr_p}"
  tags = {
    Name = "${var.env.env_name}_private_1c"
  }
}

### InternetGateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${var.env.env_name}_igw"
  }
}

### RouteTable_public
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "${var.env.env_name}_public_route_table"
  }
}

resource "aws_route_table_association" "public_1a" {
  subnet_id      = aws_subnet.public_1a.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "public_1c" {
  subnet_id      = aws_subnet.public_1c.id
  route_table_id = aws_route_table.public.id
}

### RouteTable_private
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${var.env.env_name}_private_route_table"
  }
}

resource "aws_route_table_association" "private_1a" {
  subnet_id      = aws_subnet.private_1a.id
  route_table_id = aws_route_table.private.id
}

resource "aws_route_table_association" "private_1c" {
  subnet_id      = aws_subnet.private_1c.id
  route_table_id = aws_route_table.private.id
}

### VPCEndpoint SecretManager
resource "aws_vpc_endpoint" "secretsmanager" {
  vpc_id            = aws_vpc.vpc.id
  service_name      = "com.amazonaws.ap-northeast-1.secretsmanager"
  vpc_endpoint_type = "Interface"
  subnet_ids        = [aws_subnet.private_1a.id, aws_subnet.private_1c.id]

  security_group_ids = [aws_security_group.example_vpce_sg.id]
}

### SecurityGroup(VPCEndpoint)
resource "aws_security_group" "example_vpce_sg" {
  name        = "example_vpce_sg"
  description = "Allow RDS"
  vpc_id      = aws_vpc.vpc.id

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

resource "aws_security_group_rule" "egress_vpce_all" {
  type        = "egress"
  from_port   = 0
  to_port     = 0
  protocol    = "-1"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.example_vpce_sg.id
}

### SecurityGroup(RDS Proxy)
resource "aws_security_group" "example_rdsp_sg" {
  name        = "example_rdsp_sg"
  description = "RDS Proxy"
  vpc_id      = aws_vpc.vpc.id

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

resource "aws_security_group_rule" "egress_rdsp_all" {
  type        = "egress"
  from_port   = 0
  to_port     = 0
  protocol    = "-1"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.example_rdsp_sg.id
}

### SecurityGroup(RDS)
resource "aws_security_group" "example_rds_sg" {
  name        = "example_rds_sg"
  description = "RDS"
  vpc_id      = aws_vpc.vpc.id

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

### SecurityGroup(EC2)
resource "aws_security_group" "example_ec2_sg" {
  name        = "example_ec2_sg"
  vpc_id      = aws_vpc.vpc.id
}

resource "aws_security_group_rule" "ingress_ec2_http" {
  type        = "ingress"
  from_port   = 80
  to_port     = 80
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.example_ec2_sg.id
}

resource "aws_security_group_rule" "ingress_ec2_ssh" {
  type        = "ingress"
  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.example_ec2_sg.id
}

resource "aws_security_group_rule" "egress_ec2_all" {
  type        = "egress"
  from_port   = 0
  to_port     = 0
  protocol    = "-1"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.example_ec2_sg.id
}

### IAM Role(RDS)
resource "aws_iam_role" "example_rds_iam_role" {
  name = "example-rds-iam-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "rds.amazonaws.com"
        },
      },
    ],
  })
}

# Secrets Managerへのアクセスを許可するIAMポリシー
resource "aws_iam_policy" "secretsmanager_access" {
  name        = "secretsmanager_access_policy"
  description = "Policy to allow RDS Proxy to access Secrets Manager"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = [
          "secretsmanager:GetSecretValue",
          "secretsmanager:DescribeSecret"
        ],
        Effect   = "Allow",
        Resource = "*"
      }
    ]
  })
}

# IAMポリシーをRDS ProxyのIAMロールにアタッチ
resource "aws_iam_role_policy_attachment" "secretsmanager_access_attachment" {
  role       = aws_iam_role.example_rds_iam_role.name
  policy_arn = aws_iam_policy.secretsmanager_access.arn
}

### IAM Role(EC2)
resource "aws_iam_role" "example-ec2-ssm-role" {
  name               = "example-ec2-ssm-role"
  assume_role_policy = data.aws_iam_policy_document.example-ec2-assume-role.json
}

data "aws_iam_policy_document" "example-ec2-assume-role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service" 
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

data "aws_iam_policy" "example-ec2-policy_ssm_managed_instance_core" {
  arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role_policy_attachment" "example-ec2-ssm_managed_instance_core" {
  role       = aws_iam_role.example-ec2-ssm-role.name
  policy_arn = data.aws_iam_policy.example-ec2-policy_ssm_managed_instance_core.arn
}

resource "aws_iam_instance_profile" "example-ec2-profile" {
  name = "example-ec2-profile"
  role = aws_iam_role.example-ec2-ssm-role.name
}

### EC2
resource "aws_instance" "example" {
  ami           = "ami-06180cd4edb6844d2"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.public_1a.id
  security_groups = [aws_security_group.example_ec2_sg.id]
  associate_public_ip_address = true
  iam_instance_profile = aws_iam_instance_profile.example-ec2-profile.name

  user_data = <<-EOF
                #!/bin/bash
                sudo yum update -y
                sudo amazon-linux-extras install epel -y
                sudo yum install nginx -y
                sudo systemctl start nginx
                sudo systemctl enable nginx
                sudo yum install mysql -y
              EOF
              
  tags = {
    Name = "example-instance"
  }
}

### SecretManager
resource "aws_secretsmanager_secret" "example" {
  name = "mysecret"
}

resource "aws_secretsmanager_secret_version" "example" {
  secret_id     = aws_secretsmanager_secret.example.id
  secret_string = "{\"username\":\"user\",\"password\":\"mypassword\"}"
}

### RDS Proxy
resource "aws_db_proxy" "example" {
  name                   = "example"
  debug_logging          = false
  engine_family          = "MYSQL"
  idle_client_timeout    = 1800
  require_tls            = false
  role_arn               = aws_iam_role.example_rds_iam_role.arn
  vpc_security_group_ids = [aws_security_group.example_rdsp_sg.id]
  vpc_subnet_ids         = [aws_subnet.private_1a.id, aws_subnet.private_1c.id]

  auth {
    auth_scheme = "SECRETS"
    description = "example"
    iam_auth    = "DISABLED"
    secret_arn  = aws_secretsmanager_secret.example.arn
  }
}

# RDS Proxyターゲットグループ
resource "aws_db_proxy_default_target_group" "default" {
  db_proxy_name = aws_db_proxy.example.name
}

resource "aws_db_proxy_target" "example" {
  db_proxy_name = aws_db_proxy.example.name
  target_group_name = "default"
  db_cluster_identifier = aws_rds_cluster.aurora_cluster.id
  ## ここがdb_cluster_identifierに代わって値はAuroraのものに変更
}

### RDS
## ここがClusterの設定に変更
resource "aws_rds_cluster" "aurora_cluster" {
  cluster_identifier = "aurora-cluster-demo"
  engine             = "aurora-mysql"
  engine_version     = "5.7.mysql_aurora.2.07.10"
  database_name      = "mydb"
  master_username    = "user"
  master_password    = "mypassword"
  db_subnet_group_name = aws_db_subnet_group.example.name
  vpc_security_group_ids = [aws_security_group.example_rds_sg.id]
  skip_final_snapshot = true
}

## ここがClusterの設定に変更
resource "aws_rds_cluster_instance" "aurora_instances" {
  count              = 2
  identifier         = "aurora-instance-${count.index}"
  cluster_identifier = aws_rds_cluster.aurora_cluster.id
  instance_class     = "db.r4.large"
  engine             = "aurora-mysql"
  engine_version     = "5.7.mysql_aurora.2.07.10"
}

resource "aws_db_subnet_group" "example" {
  name       = "my-db-subnet-group"
  subnet_ids = [aws_subnet.private_1a.id, aws_subnet.private_1c.id]

  tags = {
    Name = "My DB Subnet Group"
  }
}

1-2.terraform実行

# terraform plan
# terraform apply


2.MySQL接続確認
2-1.[EC2] - 作成されたEC2インスタンスを選択 - [接続]

2-2.[接続]

2-3.接続できることを確認

2-4.RDS Proxyのエンドポイントへmysqlコマンドで接続し、接続できることを確認

sh-4.2$ mysql -h example.proxy-ckmgc9vjvwrf.ap-northeast-1.rds.amazonaws.com -u user -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 1617414075
Server version: 5.7.12 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> exit
Bye



terraformで何度か作り直しをする場合、下記コマンドをCloudShellから実行しSecretManagerのシークレットを削除すること。
※terraformで削除しても7日間は使えない状態で残っていて、再作成した際にはすでにあるというエラーになるのでCLIでバッサリ削除する。

$ aws secretsmanager delete-secret --secret-id mysecret --force-delete-without-recovery
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:790301748424:secret:mysecret-3Q9YYz",
    "Name": "mysecret",
    "DeletionDate": "2023-11-23T16:12:26.020000+00:00"
}



感想

半日かかった。。。