codepipeline (#30)

* codepipeline
This commit is contained in:
Edward Viaene
2020-02-20 21:43:59 +01:00
committed by GitHub
parent cc37d59c87
commit 04cc267e86
12 changed files with 293 additions and 39 deletions
+10
View File
@@ -0,0 +1,10 @@
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
# <TASK_DEFINITION> will be replaced by codedeploy when the pipeline runs
TaskDefinition: "<TASK_DEFINITION>"
LoadBalancerInfo:
ContainerName: demo
ContainerPort: 3000
@@ -0,0 +1,21 @@
version: 0.2
phases:
pre_build:
commands:
- $(aws ecr get-login --no-include-email)
build:
commands:
- docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$CODEBUILD_RESOLVED_SOURCE_VERSION .
post_build:
commands:
# push
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$CODEBUILD_RESOLVED_SOURCE_VERSION
# create new task definition
- ./create-new-task-def.sh $IMAGE_REPO_NAME
- ./set-network-configuration.sh $IMAGE_REPO_NAME
artifacts:
files:
- 'appspec.yaml'
- 'taskdef.json'
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -ex
SERVICE_NAME=$1
IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$CODEBUILD_RESOLVED_SOURCE_VERSION"
TASKDEF_NAME=$(aws ecs list-task-definitions |jq --raw-output '.taskDefinitionArns[] | select(contains("'${SERVICE_NAME}'"))' | tail -n1)
CURRENT_TASKDEF=`aws ecs describe-task-definition --task-definition $TASKDEF_NAME`
CURRENT_TASKDEF_CONTAINERDEF=`echo $CURRENT_TASKDEF| jq --raw-output ".taskDefinition.containerDefinitions"`
TASKDEF_ROLE_ARN=`echo $CURRENT_TASKDEF| jq --raw-output ".taskDefinition.taskRoleArn"`
EXECUTION_ROLE_ARN=`echo $CURRENT_TASKDEF| jq --raw-output ".taskDefinition.executionRoleArn"`
TASKDEF=`echo $CURRENT_TASKDEF_CONTAINERDEF | jq ' [ .[] | .image = "'${IMAGE_URI}'" ]'`
CPU=$(echo $CURRENT_TASKDEF |jq -r '.taskDefinition.cpu')
MEMORY=$(echo $CURRENT_TASKDEF |jq -r '.taskDefinition.memory')
NETWORK_MODE=$(echo $CURRENT_TASKDEF |jq -r '.taskDefinition.networkMode')
REQUIRES_COMPATIBILITIES=$(echo $CURRENT_TASKDEF |jq '.taskDefinition.requiresCompatibilities[]' | tr '\n' ',' | sed 's/.$//')
echo '{"family": "'${SERVICE_NAME}'", "taskRoleArn": "'${TASKDEF_ROLE_ARN}'", "executionRoleArn": "'${EXECUTION_ROLE_ARN}'", "containerDefinitions": '$TASKDEF', "cpu": "'$CPU'", "memory": "'$MEMORY'", "requiresCompatibilities": ['$REQUIRES_COMPATIBILITIES'], "networkMode": "'${NETWORK_MODE}'" }' > taskdef.json
#aws ecs register-task-definition --cli-input-json file://task-def-template.json.new > task-def-template.json.out
#NEW_TASKDEF_ARN=`cat task-def-template.json.out |jq -r '.taskDefinition.taskDefinitionArn'`
sed -i 's#$TASKDEF#'$NEW_TASKDEF_ARN'#' appspec.yaml
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
# set subnet, security groups, public ip
SERVICE_NAME=$1
CLUSTER_NAME=$1
NETWORK_CONFIGURATION=$(aws ecs describe-services --services $SERVICE_NAME --cluster $CLUSTER_NAME |jq -r '.services[].networkConfiguration')
SUBNETS=$(echo $NETWORK_CONFIGURATION |jq '.awsvpcConfiguration.subnets[]' | tr '\n' ',' | sed 's/.$//')
SECURITY_GROUPS=$(echo $NETWORK_CONFIGURATION |jq '.awsvpcConfiguration.securityGroups[]' | tr '\n' ',' | sed 's/.$//')
PUBLIC_IP=$(echo $NETWORK_CONFIGURATION |jq '.awsvpcConfiguration.assignPublicIp')
sed -i 's/$SUBNETS/'$SUBNETS'/' appspec.yaml
sed -i 's/$SECURITY_GROUPS/'$SECURITY_GROUPS'/' appspec.yaml
sed -i 's/$PUBLIC_IP/'$PUBLIC_IP'/' appspec.yaml
+53
View File
@@ -0,0 +1,53 @@
resource "aws_codedeploy_app" "demo" {
compute_platform = "ECS"
name = "demo"
}
resource "aws_codedeploy_deployment_group" "demo" {
app_name = aws_codedeploy_app.demo.name
deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
deployment_group_name = "demo"
service_role_arn = aws_iam_role.demo-codedeploy.arn
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT"
}
terminate_blue_instances_on_deployment_success {
action = "TERMINATE"
termination_wait_time_in_minutes = 5
}
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type = "BLUE_GREEN"
}
ecs_service {
cluster_name = aws_ecs_cluster.demo.name
service_name = aws_ecs_service.demo.name
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route {
listener_arns = [aws_lb_listener.demo.arn]
}
target_group {
name = aws_lb_target_group.demo-blue.name
}
target_group {
name = aws_lb_target_group.demo-green.name
}
}
}
}
+8 -8
View File
@@ -26,8 +26,8 @@ resource "aws_codepipeline" "demo" {
output_artifacts = ["demo-docker-source"] output_artifacts = ["demo-docker-source"]
configuration = { configuration = {
RepositoryName = aws_codecommit_repository.demo.repository_name RepositoryName = aws_codecommit_repository.demo.repository_name
BranchName = "master" BranchName = "master"
} }
} }
} }
@@ -54,19 +54,19 @@ resource "aws_codepipeline" "demo" {
name = "Deploy" name = "Deploy"
action { action {
name = "Deploy" name = "DeployToECS"
category = "Deploy" category = "Deploy"
owner = "AWS" owner = "AWS"
provider = "ECS" provider = "CodeDeployToECS"
input_artifacts = ["demo-docker-build"] input_artifacts = ["demo-docker-build"]
version = "1" version = "1"
configuration = { configuration = {
ClusterName = "demo" # name of cluster ApplicationName = aws_codedeploy_app.demo.name
ServiceName = "demo" # name of service DeploymentGroupName = aws_codedeploy_deployment_group.demo.deployment_group_name
TaskDefinitionTemplateArtifact = "demo-docker-build"
AppSpecTemplateArtifact = "demo-docker-build"
} }
role_arn = aws_iam_role.demo-codepipeline.arn
} }
} }
} }
+37 -19
View File
@@ -1,10 +1,10 @@
resource "aws_ecs_task_definition" "demo" { resource "aws_ecs_task_definition" "demo" {
family = "demo" family = "demo"
execution_role_arn = aws_iam_role.ecs-task-execution-role.arn execution_role_arn = aws_iam_role.ecs-task-execution-role.arn
task_role_arn = aws_iam_role.ecs-demo-task-role.arn task_role_arn = aws_iam_role.ecs-demo-task-role.arn
cpu = 256 cpu = 256
memory = 512 memory = 512
network_mode = "awsvpc" network_mode = "awsvpc"
requires_compatibilities = [ requires_compatibilities = [
"FARGATE" "FARGATE"
] ]
@@ -39,42 +39,60 @@ DEFINITION
} }
resource "aws_ecs_service" "demo" { resource "aws_ecs_service" "demo" {
name = "demo" name = "demo"
cluster = aws_ecs_cluster.demo.id cluster = aws_ecs_cluster.demo.id
desired_count = 1 desired_count = 1
task_definition = aws_ecs_task_definition.demo.arn task_definition = aws_ecs_task_definition.demo.arn
launch_type = "FARGATE" launch_type = "FARGATE"
depends_on = [aws_lb_listener.demo]
deployment_controller {
type = "CODE_DEPLOY"
}
network_configuration { network_configuration {
subnets = slice(module.vpc.public_subnets, 1, 2) subnets = slice(module.vpc.public_subnets, 1, 2)
security_groups = [aws_security_group.ecs-demo.id] security_groups = [aws_security_group.ecs-demo.id]
assign_public_ip = true assign_public_ip = true
} }
load_balancer { load_balancer {
target_group_arn = aws_lb_target_group.demo.id target_group_arn = aws_lb_target_group.demo-blue.id
container_name = "demo" container_name = "demo"
container_port = "3000" container_port = "3000"
} }
lifecycle { lifecycle {
ignore_changes = [ ignore_changes = [
task_definition task_definition,
load_balancer
] ]
} }
} }
# security group # security group
resource "aws_security_group" "ecs-demo" { resource "aws_security_group" "ecs-demo" {
name = "ECS demo" name = "ECS demo"
vpc_id = module.vpc.vpc_id vpc_id = module.vpc.vpc_id
description = "ECS demo" description = "ECS demo"
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress { egress {
from_port = 0 from_port = 0
to_port = 0 to_port = 0
protocol = "-1" protocol = "-1"
cidr_blocks = [ cidr_blocks = [
"0.0.0.0/0" "0.0.0.0/0"
] ]
} }
} }
# logs
resource "aws_cloudwatch_log_group" "demo" {
name = "demo"
}
+11
View File
@@ -114,6 +114,17 @@ resource "aws_iam_role_policy" "demo-codebuild" {
"*" "*"
] ]
}, },
{
"Sid": "ECS",
"Effect": "Allow",
"Action": [
"ecs:List*",
"ecs:Describe*"
],
"Resource": [
"*"
]
},
{ {
"Effect": "Allow", "Effect": "Allow",
"Action": [ "Action": [
+86
View File
@@ -0,0 +1,86 @@
resource "aws_iam_role" "demo-codedeploy" {
name = "demo-codedeploy"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
data "aws_iam_policy_document" "demo-codedeploy-role-policy" {
statement {
effect = "Allow"
actions = [
"ecs:DescribeServices",
"ecs:CreateTaskSet",
"ecs:UpdateServicePrimaryTaskSet",
"ecs:DeleteTaskSet",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:ModifyRule",
"lambda:InvokeFunction",
"cloudwatch:DescribeAlarms",
"sns:Publish",
"s3:GetObject",
"s3:GetObjectVersion"
]
resources = [
"*",
]
}
statement {
effect = "Allow"
actions = [
"s3:Get*",
]
resources = [
"${aws_s3_bucket.demo-artifacts.arn}/*",
]
}
statement {
effect = "Allow"
actions = [
"kms:DescribeKey",
"kms:Decrypt",
]
resources = [
aws_kms_key.demo-artifacts.arn
]
}
statement {
effect = "Allow"
actions = [
"iam:PassRole"
]
resources = [
aws_iam_role.ecs-task-execution-role.arn,
aws_iam_role.ecs-demo-task-role.arn,
]
condition {
test = "StringLike"
variable = "iam:PassedToService"
values = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role_policy" "demo-codedeploy" {
name = "codedeploy-policy"
role = aws_iam_role.demo-codedeploy.id
policy = data.aws_iam_policy_document.demo-codedeploy-role-policy.json
}
+9 -4
View File
@@ -78,6 +78,7 @@ data "aws_iam_policy_document" "demo-codepipeline-role-policy" {
statement { statement {
effect = "Allow" effect = "Allow"
actions = [ actions = [
"codedeploy:*",
"ecs:*", "ecs:*",
] ]
resources = [ resources = [
@@ -87,19 +88,23 @@ data "aws_iam_policy_document" "demo-codepipeline-role-policy" {
statement { statement {
effect = "Allow" effect = "Allow"
actions = [ actions = [
"iam:PassRole", "iam:PassRole"
] ]
resources = [ resources = [
aws_iam_role.ecs-task-execution-role.arn, aws_iam_role.ecs-task-execution-role.arn,
aws_iam_role.ecs-demo-task-role.arn, aws_iam_role.ecs-demo-task-role.arn,
] ]
condition {
test = "StringLike"
variable = "iam:PassedToService"
values = ["ecs-tasks.amazonaws.com"]
}
} }
} }
resource "aws_iam_role_policy" "demo-codepipeline" { resource "aws_iam_role_policy" "demo-codepipeline" {
name = "codepipeline-policy" name = "codepipeline-policy"
role = aws_iam_role.demo-codepipeline.id role = aws_iam_role.demo-codepipeline.id
policy = data.aws_iam_policy_document.demo-codepipeline-role-policy.json policy = data.aws_iam_policy_document.demo-codepipeline-role-policy.json
} }
+27 -6
View File
@@ -1,7 +1,8 @@
resource "aws_lb" "demo" { resource "aws_lb" "demo" {
name = "demo" name = "demo"
subnets = module.vpc.public_subnets subnets = module.vpc.public_subnets
load_balancer_type = "network" load_balancer_type = "network"
enable_cross_zone_load_balancing = true
} }
resource "aws_lb_listener" "demo" { resource "aws_lb_listener" "demo" {
@@ -10,13 +11,33 @@ resource "aws_lb_listener" "demo" {
protocol = "TCP" protocol = "TCP"
default_action { default_action {
target_group_arn = aws_lb_target_group.demo.id target_group_arn = aws_lb_target_group.demo-blue.id
type = "forward" type = "forward"
} }
lifecycle {
ignore_changes = [
default_action,
]
}
} }
resource "aws_lb_target_group" "demo" { resource "aws_lb_target_group" "demo-blue" {
name = "demo-http" name = "demo-http-blue"
port = "3000"
protocol = "TCP"
target_type = "ip"
vpc_id = module.vpc.vpc_id
deregistration_delay = "30"
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
protocol = "TCP"
interval = 30
}
}
resource "aws_lb_target_group" "demo-green" {
name = "demo-http-green"
port = "3000" port = "3000"
protocol = "TCP" protocol = "TCP"
target_type = "ip" target_type = "ip"
+1 -1
View File
@@ -13,6 +13,6 @@ module "vpc" {
enable_vpn_gateway = false enable_vpn_gateway = false
tags = { tags = {
"Name" = "terraform-cloudpipeline-demo" "Name" = "terraform-cloudpipeline-demo"
} }
} }