0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS RAMを使ったAWS CodeConnections共有をTerraformで自動化する

Posted at

はじめに

AWS CodeConnectionsシリーズ第2弾。
過去の記事では、GitLab Self Managedとの接続をAWS CodeConnectionsで行ったが、今回はGitHub(非Enterprise)との接続を行う。

さらに、2025年3月6日には、AWS Resource Access Manager(RAM)を使った簡単なクロスアカウントのAWS CodeConnections共有ができるようになったことで、従来、環境が増えるたびにマネージメントコンソール画面を使ってリソースの作成を完了させる必要があったが、管理アカウントのみの対応で済むようになった。
今回の記事では、以下の構成図の通り、この新しいリソース共有方法も試してみることにする。

image.png

関連のリンクはこちら

また、過去のAWS CodeConnections関連記事は以下を参照していただきたい。

なお、本記事を読むにあたっての前提知識は以下の2点である。

  • AWS CodePipelineとAWS CodeConnectionsを触ったことがある
  • 初歩的なTerraformを読んで内容を理解することができる

GitHubに接続するAWS CodeConnections

AWS CodeConnectionsでGitHubに接続するには、以下の通りaws_codeconnections_connectionのみ定義すれば良い。GitLab Self Managedで使っていたaws_codeconnections_hostについては必要ない。

resource "aws_codeconnections_connection" "example" {
  name          = local.codeconnections_connection_name
  provider_type = "GitHub"
}

このリソースを作った直後は、まだ接続が完了していないためすぐには使えない。
AWSマネージメントコンソールで作成したリソースを選択し、AWS公式ユーザーガイドの

2. AWS コネクタの承認 GitHubを選択します。接続ページが表示され、GitHub アプリフィールドが表示されます。

以降の作業を行う。この承認行為のみ、AWSの公開APIが提供されていないため、今回のTerraformによる自動化の範囲外となる。

また、以下の項ではクロスアカウント対象のアカウントが必要になってくるので、以下のデータソースも宣言しておこう。

provider "aws" {
  alias   = "crossaccount"
  profile = "crossaccount"
  region  = "ap-northeast-1"
}


data "aws_caller_identity" "crossaccount" {
  provider = aws.crossaccount
}

AWS Resource Access Managerの設定

管理アカウント側の設定

ここはそんなに難しい部分はない。Terraformの公式ドキュメントとほぼ同じ内容になるはずで、aws_ram_resource_shareを作成し、アクセス元アカウントのプリンシパルについてaws_ram_principal_associationと連携させ、aws_ram_resource_associationもろとも紐づけるイメージだ。

共有先のアカウントを自分のOrganizations外にする場合は、allow_external_principals = trueに設定する必要があるので注意。

resource "aws_ram_resource_share" "example" {
  name                      = local.ram_share_name
  allow_external_principals = true
}

resource "aws_ram_resource_association" "example" {
  resource_share_arn = aws_ram_resource_share.example.arn
  resource_arn       = aws_codeconnections_connection.example.id
}

resource "aws_ram_principal_association" "example" {
  resource_share_arn = aws_ram_resource_share.example.arn
  principal          = data.aws_caller_identity.crossaccount.account_id
}

クロスアカウント側の設定

管理アカウントアカウント以外に、クロスアカウントでも接続の招待を受けて承認できるようにしておく必要があるため、以下のaws_ram_resource_share_accepterを行う。ここはマネージメントコンソール不要で、TerraformのみでスムースにApplyをすることができる。

resource "aws_ram_resource_share_accepter" "example" {
  provider = aws.crossaccount

  share_arn = aws_ram_principal_association.example.resource_share_arn
}

AWS CodePipelineでの利用方法

AWS RAMの設定が完了したら、あとはクロスアカウント対象のアカウントのリソースを作っていこう。

AWS CodePipeline周辺リソース

Amazon CloudWatch Logs

AWS CodeBuildのデバッグ用に作っておく。

resource "aws_cloudwatch_log_group" "codebuild_crossaccount" {
  provider = aws.crossaccount

  name = local.clouddwatch_loggroup_name
}

Amazon S3

Amazon S3は普通にAWS CodePipelineからアクセス可能なPrivateな箱を作っておけばよい。

resource "aws_s3_bucket" "example_crossaccount" {
  provider = aws.crossaccount

  bucket = local.s3_bucket_crossaccount_name

  force_destroy = true
}

resource "aws_s3_bucket_ownership_controls" "example_crossaccount" {
  provider = aws.crossaccount

  bucket = aws_s3_bucket.example_crossaccount.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

resource "aws_s3_bucket_public_access_block" "example_crossaccount" {
  provider = aws.crossaccount

  bucket = aws_s3_bucket.example_crossaccount.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

IAM

IAMは、AWS CodePipelineとAWS CodeBuildについて作成する。
ポイントはcodestar-connections:UseConnectionで管理アカウントのリソースを設定する部分だ。
それ以外は特に難しいところはない。

################################################################################
# IAM for AWS CodeBuild                                                        #
################################################################################
resource "aws_iam_role" "codebuild_crossaccount" {
  provider = aws.crossaccount

  name               = local.iam_codebuild_role_name
  assume_role_policy = data.aws_iam_policy_document.codebuild_crossaccount_assume.json
}

data "aws_iam_policy_document" "codebuild_crossaccount_assume" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

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

resource "aws_iam_role_policy" "codebuild_crossaccount" {
  provider = aws.crossaccount

  name   = local.iam_codebuild_policy_name
  role   = aws_iam_role.codebuild_crossaccount.id
  policy = data.aws_iam_policy_document.codebuild_crossaccount_custom.json
}

data "aws_iam_policy_document" "codebuild_crossaccount_custom" {
  statement {
    effect = "Allow"

    actions = [
      "codeBuild:StartBuild",
    ]

    resources = [
      aws_codebuild_project.example_crossaccount.arn,
    ]
  }
  statement {
    effect = "Allow"

    actions = [
      "s3:GetObject",
    ]

    resources = [
      "${aws_s3_bucket.example_crossaccount.arn}/*",
    ]
  }
  statement {
    effect = "Allow"

    actions = [
      "logs:CreateLogStream",
      "logs:PutLogEvents",
    ]

    resources = [
      aws_cloudwatch_log_group.codebuild_crossaccount.arn,
      "${aws_cloudwatch_log_group.codebuild_crossaccount.arn}:log-stream:*",
    ]
  }
}

################################################################################
# IAM for AWS CodePipeline                                                     #
################################################################################
resource "aws_iam_role" "codepipeline_crossaccount" {
  provider = aws.crossaccount

  name               = local.iam_codepipeline_role_name
  assume_role_policy = data.aws_iam_policy_document.codepipeline_crossaccount_assume.json
}

data "aws_iam_policy_document" "codepipeline_crossaccount_assume" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

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

resource "aws_iam_role_policy" "codepipeline_crossaccount" {
  provider = aws.crossaccount

  name   = local.iam_codepipeline_policy_name
  role   = aws_iam_role.codepipeline_crossaccount.id
  policy = data.aws_iam_policy_document.codepipeline_crossaccount_custom.json
}

data "aws_iam_policy_document" "codepipeline_crossaccount_custom" {
  statement {
    effect = "Allow"

    actions = [
      "codebuild:StartBuild",
      "codebuild:StopBuild",
      "codebuild:BatchGet*",
      "codebuild:Get*",
      "codebuild:List*",
      "s3:*",
    ]

    resources = [
      "*",
    ]
  }

  statement {
    sid = "AllowCodeStarConnections"

    effect = "Allow"

    actions = [
      "codestar-connections:UseConnection",
    ]

    resources = [
      aws_codeconnections_connection.example.arn,
    ]
  }
}

AWS CodeBuild

################################################################################
# AWS CodeBuild                                                                #
################################################################################
resource "aws_codebuild_project" "example_crossaccount" {
  provider = aws.crossaccount

  name         = local.codebuild_project_name
  service_role = aws_iam_role.codebuild_crossaccount.arn

  source {
    type      = "CODEPIPELINE"
    buildspec = "buildspec.yml"
  }

  artifacts {
    type = "CODEPIPELINE"
  }

  environment {
    type            = "LINUX_CONTAINER"
    compute_type    = "BUILD_GENERAL1_SMALL"
    image           = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
  }

  logs_config {
    cloudwatch_logs {
      group_name = aws_cloudwatch_log_group.codebuild_crossaccount.name
    }
  }
}

AWS CodePipeline本体

いよいよAWS CodePipeline本体の作成だ。

AWS CodePipelineでAWS CodeConnectionsを利用する方法は、GitLab Self Managed利用時と大差ない。
trigger.provider_typeCodeStarSourceConnectionに設定するのと、stage.action.configuration.ConnectionArnを先に作成しているAWS CodeConnectionの接続のARNを設定しけばよい。

resource "aws_codepipeline" "example_crossaccount" {
  provider = aws.crossaccount

  name     = local.codepipeline_pipeline_name
  role_arn = aws_iam_role.codepipeline_crossaccount.arn

  pipeline_type = "V2"

  trigger {
    provider_type = "CodeStarSourceConnection"
    git_configuration {
      source_action_name = "Source"
      push {
        branches {
          includes = ["main"]
        }
      }
    }
  }

  artifact_store {
    type     = "S3"
    location = aws_s3_bucket.example_crossaccount.bucket
  }

  stage {
    name = "Source"

    action {
      run_order        = 1
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = "1"
      output_artifacts = ["SourceArtifact"]
      namespace        = "SourceVariables"

      configuration = {
        ConnectionArn    = aws_codeconnections_connection.example.arn
        FullRepositoryId = "${var.github_name}/example"
        BranchName       = "staging"
      }
    }
  }

  stage {
    name = "Build"

    action {
      run_order       = 2
      name            = "Build"
      category        = "Build"
      owner           = "AWS"
      provider        = "CodeBuild"
      version         = "1"
      input_artifacts = ["SourceArtifact"]

      configuration = {
        ProjectName = aws_codebuild_project.example_crossaccount.name
        EnvironmentVariables = jsonencode([
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_AUTHORDATE"
            value = "#{SourceVariables.AuthorDate}"
          },
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_BRANCHNAME"
            value = "#{SourceVariables.BranchName}"
          },
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_COMMITID"
            value = "#{SourceVariables.CommitId}"
          },
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_COMMITMESSAGE"
            value = "#{SourceVariables.CommitMessage}"
          },
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_CONNECTIONARN"
            value = "#{SourceVariables.ConnectionArn}"
          },
          {
            type  = "PLAINTEXT"
            name  = "CODEPIPELINE_SRCVAR_FULLREPOSITORYNAME"
            value = "#{SourceVariables.FullRepositoryName}"
          },
        ])
      }
    }
  }
}

ビルドでは、各種環境変数をGitHubが設定してくれているので、それぞれ内容を確認してみる。

なお、今回網羅はしたので改めて確認する必要はないが、利用可能な環境変数の一覧については、ユーザーガイドに記載がある。「CodeStarSourceConnection アクションの出力変数」を確認していただけば良い。

これらの環境変数を参照するために、Buildspecは以下のように作成してみよう。

buildspec.yml
version: 0.2

phases:
  build:
    commands:
      - echo ${CODEPIPELINE_SRCVAR_AUTHORDATE}
      - echo ${CODEPIPELINE_SRCVAR_BRANCHNAME}
      - echo ${CODEPIPELINE_SRCVAR_COMMITID}
      - echo ${CODEPIPELINE_SRCVAR_COMMITMESSAGE}
      - echo ${CODEPIPELINE_SRCVAR_CONNECTIONARN}
      - echo ${CODEPIPELINE_SRCVAR_FULLREPOSITORYNAME}

上記Buildspecメインブランチを更新すると、ビルドログに環境変数が出力される。
上記した環境変数設定が設定され、上手く機能している証拠だ。

[Container] 2025/03/09 11:12:40.650869 Running command echo ${CODEPIPELINE_SRCVAR_AUTHORDATE}
2025-03-09T11:12:16Z

[Container] 2025/03/09 11:12:40.656947 Running command echo ${CODEPIPELINE_SRCVAR_BRANCHNAME}
main

[Container] 2025/03/09 11:12:40.662338 Running command echo ${CODEPIPELINE_SRCVAR_COMMITID}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

[Container] 2025/03/09 11:12:40.667594 Running command echo ${CODEPIPELINE_SRCVAR_COMMITMESSAGE}
Initial commit.

[Container] 2025/03/09 11:12:40.672782 Running command echo ${CODEPIPELINE_SRCVAR_CONNECTIONARN}
arn:aws:codeconnections:ap-northeast-1:XXXXXXXXXXXX:connection/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

[Container] 2025/03/09 11:12:40.677835 Running command echo ${CODEPIPELINE_SRCVAR_FULLREPOSITORYNAME}
neruneruo/example

これで、AWS CodeConnectionsを用いた接続方法の選択肢が増えた、
パイプラインのソースにGitHubを使って、より良い開発環境を整えていこう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?