operations: Complete migration to AWS ECS.

This commit is contained in:
Jonathan Bernard 2021-07-05 01:57:39 -05:00
parent 99a4c1fc94
commit bc06fc54bb
12 changed files with 232 additions and 166 deletions

View File

@ -1,10 +0,0 @@
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals>api</KeyPrefixEquals>
</Condition>
<Redirect>
<HostName>https://pmapi.jdbernard.com</HostName>
</Redirect>
</RoutingRule>
</RoutingRules>

View File

@ -1,33 +0,0 @@
server {
listen 80;
server_name pmapi-dev.jdb-labs.com;
return 301 https://pmapi-dev.jdb-labs.com$request_uri;
}
server {
listen 443;
server_name pmapi-dev.jdb-labs.com;
ssl on;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://pm-dev.jdb-labs.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
return 204;
}
proxy_pass http://localhost:8281;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

View File

@ -1,33 +0,0 @@
server {
listen 80;
server_name pmapi.jdb-labs.com;
return 301 https://pmapi.jdb-labs.com$request_uri;
}
server {
listen 443;
server_name pmapi.jdb-labs.com;
ssl on;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://pm.jdb-labs.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
return 204;
}
proxy_pass http://localhost:8280;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

View File

@ -88,11 +88,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
}
}
tags = {
Environment = var.environment
Environment = local.environment_name
}
viewer_certificate {
acm_certificate_arn = var.domain_cert.arn
acm_certificate_arn = data.terraform_remote_state.jdbsoft.outputs.aws_acm_certificate_jdbsoft_us_east_1.arn
ssl_support_method = "sni-only"
}
}

View File

@ -1,49 +1,5 @@
# provider "aws" {
# alias = "cert"
# region = "us-east-1"
# }
#
# resource "aws_acm_certificate" "cert" {
# provider = aws.cert
# domain_name = local.app_domain_name
# validation_method = "DNS"
#
# subject_alternative_names = [local.api_domain_name]
#
# tags = {
# Environment = var.environment
# }
#
# lifecycle {
# create_before_destroy = true
# }
# }
#
# resource "aws_route53_record" "cert_validation" {
# for_each {
# for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
# name = dvo.resource_record_name
# type = dvo.resource_record_type
# record = dvo.resource_record_value
# }
# }
#
# allow_overwrite = true
# name = each.value.name
# records = [ each.value.record ]
# ttl = 60
# type = each.value.type
# zone_id = var.route53_zone.zone_id
# }
#
# resource "aws_acm_certificate_validation" "cert" {
# provider = aws.cert
# certificate_arn = aws_acm_certificate.cert.arn
# validation_record_fqdns = [ for record in aws_route53_record.cert_validation : record.fqdn ]
# }
resource "aws_route53_record" "app_domain" {
zone_id = var.route53_zone.zone_id
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft.zone_id
name = local.app_domain_name
type = "A"
@ -56,10 +12,14 @@ resource "aws_route53_record" "app_domain" {
depends_on = [aws_cloudfront_distribution.s3_distribution ]
}
# resource "aws_route53_record" "api_domain" {
# zone_id = var.route53_zone.zone_id
# name = local.api_domain_name
# type = "A"
#
# # TODO: alias configuration
# }
resource "aws_route53_record" "api_domain" {
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft.zone_id
name = local.api_domain_name
type = "A"
alias {
name = data.terraform_remote_state.jdbsoft.outputs.aws_lb_jdbsoft.dns_name
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_lb_jdbsoft.zone_id
evaluate_target_health = false
}
}

View File

@ -1,3 +1,75 @@
# resource "aws_ecs_task_definition" "pmapi" {
# family = "pmapi-dev" # TODO: parameterize based on env
# }
resource "aws_secretsmanager_secret" "pmapi_auth" {
name = "${local.environment_name}-AuthSecret"
tags = { Environment = local.environment_name }
}
resource "aws_secretsmanager_secret" "pmapi_db_conn_string" {
name = "${local.environment_name}-DbConnString"
tags = { Environment = local.environment_name }
}
resource "aws_ecs_task_definition" "pmapi" {
family = local.environment_name
network_mode = "bridge"
requires_compatibilities = ["EC2"]
execution_role_arn = aws_iam_role.ecs_task.arn
# See https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html
container_definitions = jsonencode([
{
name = local.environment_name
image = "${var.ecr_repo.repository_url}:${data.external.git_describe.result.version}"
cpu = 128
memory = 128
memoryReservation = 32
environment = [
{
name = "PORT"
value = "80"
}
]
portMappings = [
{
protocol = "tcp"
containerPort = 80
}
]
secrets = [
{
name = "AUTH_SECRET"
description = "Auth secret used to hash and salt passwords."
valueFrom = aws_secretsmanager_secret.pmapi_auth.arn
},
{
name = "DB_CONN_STRING"
description = "Connection string with user credentials."
valueFrom = aws_secretsmanager_secret.pmapi_db_conn_string.arn
}
]
}
])
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}
resource "aws_ecs_service" "pmapi" {
name = local.environment_name
cluster = data.terraform_remote_state.jdbsoft.outputs.aws_ecs_cluster_ortis.id
task_definition = aws_ecs_task_definition.pmapi.arn
desired_count = 1
launch_type = "EC2"
load_balancer {
target_group_arn = aws_lb_target_group.pmapi.arn
container_name = local.environment_name
container_port = 80
}
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}

View File

@ -0,0 +1,70 @@
resource "aws_iam_role" "ecs_task" {
name = "${local.environment_name}-EcsTaskRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
inline_policy {
name = "AllowSecretsAccessForPmApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"kms:Decrypt"
]
Resource = [
aws_secretsmanager_secret.pmapi_auth.arn,
aws_secretsmanager_secret.pmapi_db_conn_string.arn
]
}
]
})
}
inline_policy {
name = "AllowAccessToEcrForPmApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken"
]
Resource = [ "*" ]
},
{
Effect = "Allow"
Action = [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:GetDownloadUrlForLayer"
]
Resource = [
var.ecr_repo.arn
]
}
]
})
}
tags = {
Name = "PersonalMeasure-EcsTaskRole"
Environment = local.environment_name
}
}

View File

@ -0,0 +1,43 @@
resource "aws_lb_target_group" "pmapi" {
name = "${local.environment_name}-${substr(uuid(), 0, 2)}"
port = 80
protocol = "HTTP"
target_type = "instance"
vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id
health_check {
enabled = true
matcher = "200"
path = "/v0/version"
}
lifecycle {
create_before_destroy = true
ignore_changes = [name]
}
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}
resource "aws_lb_listener_rule" "pmapi" {
listener_arn = data.terraform_remote_state.jdbsoft.outputs.aws_lb_listener_https.arn
action {
type = "forward"
target_group_arn = aws_lb_target_group.pmapi.arn
}
condition {
host_header {
values = [ local.api_domain_name ]
}
}
tags = {
Name = "${local.api_domain_name} HTTPS"
Environment = local.environment_name
}
}

View File

@ -8,15 +8,27 @@ variable "artifact_bucket" {
description = "The aws_s3_bucket object representing the artifact bucket where deployed artifacts, logs, etc. live."
}
variable "domain_cert" {
description = "ACM SSL certificate to use for this environment's configuration."
}
variable "route53_zone" {
description = "Route53 hosted zone for the deployed environments."
variable "ecr_repo" {
description = "ECR repository information."
}
locals {
app_domain_name = "pm${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
api_domain_name = "api.pm${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
environment_name = "PersonalMeasure-${var.environment}"
app_domain_name = "pm${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
api_domain_name = "pmapi${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
}
data "external" "git_describe" {
program = ["sh", "-c", "git describe | xargs printf '{\"version\": \"%s\"}'"]
}
data "terraform_remote_state" "jdbsoft" {
backend = "s3"
config = {
bucket = "operations.jdb-software.com"
region = "us-west-2"
key = "terraform/operations.tfstate"
dynamodb_table = "terraform-state-lock.jdb-software.com"
}
}

View File

@ -0,0 +1,8 @@
resource "aws_ecr_repository" "personal_measure_api" {
name = "personal_measure_api"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}

View File

@ -7,23 +7,12 @@ resource "aws_s3_bucket" "personal_measure" {
acl = "log-delivery-write"
}
resource "aws_ecr_repository" "personal_measure_api" {
name = "personal_measure_api"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
module "dev_env" {
source = "./deployed_env"
environment = "dev"
artifact_bucket = aws_s3_bucket.personal_measure
route53_zone = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft
domain_cert = data.terraform_remote_state.jdbsoft.outputs.aws_acm_certificate_jdbsoft_us_east_1
ecr_repo = aws_ecr_repository.personal_measure_api
}
module "prod_env" {
@ -31,8 +20,7 @@ module "prod_env" {
environment = "prod"
artifact_bucket = aws_s3_bucket.personal_measure
route53_zone = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft
domain_cert = data.terraform_remote_state.jdbsoft.outputs.aws_acm_certificate_jdbsoft_us_east_1
ecr_repo = aws_ecr_repository.personal_measure_api
}
data "aws_iam_policy_document" "cloudfront_access_policy" {

View File

@ -6,14 +6,3 @@ terraform {
dynamodb_table = "terraform-state-lock.jdb-software.com"
}
}
data "terraform_remote_state" "jdbsoft" {
backend = "s3"
config = {
bucket = "operations.jdb-software.com"
region = "us-west-2"
key = "terraform/operations.tfstate"
dynamodb_table = "terraform-state-lock.jdb-software.com"
}
}