Jenkins와 Codebuild를 연동하게 되면, 빌드 또는 배포 job 은 jenkins에서 생성하고 실제 작업은 Codebuild를 통해서 진행하실 수 있습니다. Jenkins에서 Job이 실행되면 AWS 상에 독립적인 codebuild job이 생성 됩니다.
Copy # Jenkins User
resource "aws_iam_user" "jenkins_codebuild" {
name = "jenkins-codebuild"
}
# Permissions that jenkins needs to create codebuild job
# You can change s3 bucket below if you have any bucket that jenkins use for retrieving/uploading artifact
resource "aws_iam_user_policy" "jenkins_codebuild" {
name = "jenkins-codebuild"
user = aws_iam_user.jenkins_codebuild.name
policy = << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": ["arn:aws:logs:*:*:log-group:/aws/codebuild/*"],
"Action": ["logs:GetLogEvents"]
},
{
"Effect": "Allow",
"Resource": ["arn:aws:s3:::art-deploy"],
"Action": ["s3:GetBucketVersioning"]
},
{
"Effect": "Allow",
"Resource": ["arn:aws:s3:::art-deploy/*"],
"Action": ["s3:PutObject"]
},
{
"Effect": "Allow",
"Resource": ["arn:aws:s3:::art-deploy/*"],
"Action": ["s3:GetObject"]
},
{
"Effect": "Allow",
"Resource": ["arn:aws:codebuild:*:*:*"],
"Action": [
"codebuild:StartBuild",
"codebuild:BatchGetBuilds",
"codebuild:BatchGetProjects",
"codebuild:StopBuild"
]
}
]
}
EOF
}
생성이 완료되었으면 아래와 같이 Access Key를 발급 받습니다. 발급받으신 키는 반드시 안전한 곳에 보관 하시기 바랍니다.
이제 연동할 Codebuild 프로젝트를 생성하도록 하겠습니다. Codebuild 프로젝트를 위해서는 아래의 리소스가 필요합니다.
vim terraform/iam/art-prod/codebuild-deployment.tf
Copy resource "aws_iam_role" "codebuild_deployment" {
name = "codebuild-deployment"
path = "/service-role/" assume_role_policy = << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "codebuild_deployment_operation" {
name = "codebuild-deployment-operation-access"
role = aws_iam_role.codebuild_deployment.id
policy = << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DeploymentSecurityGroupAccess",
"Action": [
"ec2:CreateSecurityGroup",
"ec2:DescribeSecurityGroups",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentAMIAccess",
"Action": [
"ec2:RegisterImage",
"ec2:DescribeImages"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentSnapshotAccess",
"Action": [
"ec2:CreateSnapshot",
"ec2:DeleteSnaphot",
"ec2:DescribeSnapshots"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentInstanceAccess",
"Action": [
"ec2:RunInstances",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:RebootInstances",
"ec2:TerminateInstances",
"ec2:DescribeInstances",
"ec2:CreateTags",
"ec2:DescribeTags",
"ec2:ModifyInstanceAttribute"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentKeyPairAccess",
"Action": [
"ec2:DescribeKeyPairs"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "LaunchTemplates",
"Action": [
"ec2:DeleteLaunchTemplate",
"ec2:CreateLaunchTemplate",
"ec2:GetLaunchTemplateData",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeLaunchTemplateVersions",
"ec2:ModifyLaunchTemplate",
"ec2:DeleteLaunchTemplateVersions",
"ec2:CreateLaunchTemplateVersion"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentVolumeAccess",
"Action": [
"ec2:AttachVolume",
"ec2:CreateVolume",
"ec2:DeleteVolume",
"ec2:DescribeVolume*",
"ec2:DetachVolume"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentASGAccess",
"Action": [
"autoscaling:*"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentVPCAccess",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeDhcpOptions",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterfacePermission"
],
"Resource": "arn:aws:ec2:ap-northeast-2:${var.account_id}:network-interface/*"
},
{
"Sid": "DeploymentIAMAccess",
"Action": [
"iam:PassRole"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "DeploymentELBAccess",
"Action": [
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeInstanceHealth"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "CloudwatchAccess",
"Action": [
"cloudwatch:PutMetricAlarm"
],
"Effect": "Allow",
"Resource": ["*"]
},
{
"Sid": "SSMSendCommand",
"Action": [
"ssm:SendCommand",
"ssm:ListCommandInvocations"
],
"Effect": "Allow",
"Resource": ["*"]
}
]
}
EOF
}
resource "aws_iam_role_policy" "codebuild_deployment_ecr" {
name = "codebuild-deployment-ecr"
role = aws_iam_role.codebuild_deployment.id
policy = << EOF
{
"Statement": [
{
"Sid": "AllowGetAuthTokenAccess",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Sid": "AllowReadECRAccess",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "*"
}
]
}
EOF
}
# If codebuild needs to deploy to another account
# then, it should have assume permission.
#resource "aws_iam_role_policy" "codebuild_deployment_assume_deploy" {
# name = "codebuild-deployment-assume-deploy"
# role = aws_iam_role.codebuild_deployment.id
# policy = <<EOF
#{
# "Statement": [
# {
# "Sid": "AllowAnsibleHeadDeployBucketAccess",
# "Action": [
# "sts:AssumeRole"
# ],
# "Resource": [
# "arn:aws:iam::<target account id>:role/deployment"
# ],
# "Effect": "Allow"
# }
# ]
#}
#EOF
#}
# If you want to store secret data to AWS Parameter Store,
# then you should give permission so that codebuild can retrieve those values for build and deployment
resource "aws_iam_role_policy" "codebuild_deployment_kms" {
name = "codebuild-deployment-kms-decryption"
role = aws_iam_role.codebuild_deployment.id
policy = << EOF
{
"Statement": [
{
"Sid": "AllowSsmParameterAccess",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters"
],
"Effect": "Allow",
"Resource": [
"arn:aws:ssm:ap-northeast-2:${var.account_id}:parameter/CodeBuild/*"
]
}
]
}
EOF
}
resource "aws_iam_role_policy" "codebuild_deployment_cloudwatch" {
name = "codebuild-deployment-cloudwatch"
role = aws_iam_role.codebuild_deployment.id
policy = << EOF
{
"Statement": [
{
"Sid": "AllowCloudWatchAccess",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:${var.account_id}:log-group:/aws/codebuild/*",
"arn:aws:logs:*:${var.account_id}:log-group:/aws/codebuild/*:*",
"arn:aws:logs:*:${var.account_id}:log-group:/*",
"arn:aws:logs:*:${var.account_id}:log-group:/*:*",
"arn:aws:logs:*:${var.account_id}:log-group:/*:*:*",
"arn:aws:logs:*:${var.account_id}:log-group:*:*:*/*"
],
"Effect": "Allow"
}
]
}
EOF
}
resource "aws_iam_instance_profile" "codebuild_deployment" {
name = "codebuild-deployment-profile"
role = aws_iam_role.codebuild_deployment.name
}
resource "aws_iam_role_policy_attachment" "codebuild_deployment_attach" {
role = aws_iam_role.codebuild_deployment.name
policy_arn = aws_iam_policy.app_universal.arn
}
output "codebuild_deployment_instance_profile" {
value = aws_iam_instance_profile.codebuild_deployment.arn
}
output "codebuild_deployment_arn" {
value = aws_iam_role.codebuild_deployment.arn
}
vim terraform/ecr/art-prod/prod_apnortheast2/art_build.tf
Copy resource "aws_ecr_repository" "art_build" {
name = "art-build"
}
resource "aws_ecr_repository_policy" "art_build" {
repository = aws_ecr_repository.art_build.name
policy = << EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "new policy",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<your account id>:root"
]
},
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeRepositories",
"ecr:GetRepositoryPolicy",
"ecr:ListImages"
]
}
]
}
EOF
}
vim terraform/vpc/artp_apnortheast2/codebuild_sg.tf
Copy # Codebuild Default Security Group
resource "aws_security_group" "codebuild_default" {
name = "codebuild-default-${var.vpc_name}"
description = "codebuild-default group for ${var.vpc_name}"
vpc_id = aws_vpc.default.id
ingress {
from_port = 0
to_port = 65535
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" ]
}
}
vim terraform/vpc/artp_apnortheast2/outputs.tf
Copy output "aws_security_group_codebuild_default_id" {
value = aws_security_group.codebuild_default.id
}
vim terraform/codebuild/artp-apnortheast2/deployment.tf
Copy module "deployment" {
source = "../_modules/deployment"
service_name = "deployment-prod"
shard_id = data.terraform_remote_state.vpc.outputs.shard_id
public_subnets = data.terraform_remote_state.vpc.outputs.public_subnets
private_subnets = data.terraform_remote_state.vpc.outputs.private_subnets
aws_region = data.terraform_remote_state.vpc.outputs.aws_region
target_vpc = data.terraform_remote_state.vpc.outputs.vpc_id
vpc_name = data.terraform_remote_state.vpc.outputs.vpc_name
vpc_cidr_numeral = data.terraform_remote_state.vpc.outputs.cidr_numeral
billing_tag = data.terraform_remote_state.vpc.outputs.billing_tag
env_suffix = data.terraform_remote_state.vpc.outputs.env_suffix
image_credentials_type = "SERVICE_ROLE"
# Change this to your security group ID
deployment_default_sg = data.terraform_remote_state.vpc.outputs.aws_security_group_codebuild_default_id
# Change this to your IAM role
service_role = "arn:aws:iam::816736805842:role/service-role/codebuild-deployment"
# Change this repository which you want to pull code from.
# In this repository, it should have buildspec file that you specify below
github_repo = "https://github.com/DevopsArtFactory/goployer.git"
# buildspec file name you want to use
buildspec = "buildspec-deploy-prod.yml"
# Change this to your docker image that you want to use for codebuild
build_image = "816736805842.dkr.ecr.ap-northeast-2.amazonaws.com/art-build:latest"
}
이제 Codebuild 연동을 위해서 Jenkins 세팅을 진행하도록 하겠습니. 본 세팅은 따로 코드로 정의하지 않았고, 아래 가이드를 통해서 한 번 설정하시고 나면 특별한 일이 없는 이상은 바꾸실 일이 거의 없습니다.
설치가 진행되면 Jenkins를 재실행 할 수 있도록 옵션을 클릭해주고 기다립니다. Jenkins가 설치를 마치면 알아서 재시작합니다.