founders_template 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile-rails6.lock +1 -1
  3. data/lib/founders_template/cli.rb +35 -4
  4. data/lib/founders_template/config_file.rb +11 -2
  5. data/lib/founders_template/version.rb +1 -1
  6. data/templates/Dockerfile.erb +68 -0
  7. data/templates/docker/nginx/nginx.conf +78 -0
  8. data/templates/docker-compose.yml.erb +75 -0
  9. data/templates/terraform/modules/application/main.tf +70 -0
  10. data/templates/terraform/modules/application/one_off_service.tf +48 -0
  11. data/templates/terraform/modules/application/outputs.tf +19 -0
  12. data/templates/terraform/modules/application/policies/ecs_execution.json +17 -0
  13. data/templates/terraform/modules/application/policies/ecs_execution_role.json +21 -0
  14. data/templates/terraform/modules/application/policies/ecs_task_execution.json +17 -0
  15. data/templates/terraform/modules/application/sshable.tf +17 -0
  16. data/templates/terraform/modules/application/task_definitions/one_off.json +18 -0
  17. data/templates/terraform/modules/application/task_definitions/web.json +41 -0
  18. data/templates/terraform/modules/application/task_definitions/worker.json +49 -0
  19. data/templates/terraform/modules/application/variables.tf +109 -0
  20. data/templates/terraform/modules/application/web_service.tf +62 -0
  21. data/templates/terraform/modules/application/worker_service.tf +48 -0
  22. data/templates/terraform/production/domains.tf +19 -0
  23. data/templates/terraform/production/main.tf +167 -0
  24. data/templates/terraform/production/ssl.tf +28 -0
  25. data/templates/terraform/production/variables.tf +36 -0
  26. data/templates/terraform/shared/main.tf +41 -0
  27. data/templates/terraform/shared/terraform.tfstate.backup +129 -0
  28. data/templates/terraform-production.tfvars.erb +4 -0
  29. data/templates/terraform-shared.tfvars.erb +1 -0
  30. metadata +26 -2
@@ -0,0 +1,109 @@
1
+ variable "name" {
2
+ description = "The name to use for resources"
3
+ type = string
4
+ }
5
+
6
+ variable "app_environment" {
7
+ description = "Environment variables to set on the application container"
8
+ default = []
9
+
10
+ type = list(object({
11
+ name = string,
12
+ value = string
13
+ }))
14
+ }
15
+
16
+ variable "worker_environment" {
17
+ description = "Environment variables to set on the worker container"
18
+ default = []
19
+
20
+ type = list(object({
21
+ name = string,
22
+ value = string
23
+ }))
24
+ }
25
+
26
+ variable "app_repository_url" {
27
+ description = "The URL to the image repository for the application container"
28
+ type = string
29
+ }
30
+
31
+ variable "web_repository_url" {
32
+ description = "The URL to the image repository for the web container"
33
+ type = string
34
+ }
35
+
36
+ variable "aws_region" {
37
+ description = "The AWS region to launch resources in"
38
+ type = string
39
+ default = "us-east-1"
40
+ }
41
+
42
+ variable "app_process_memory" {
43
+ description = "The amount of memory to allocate to the application process"
44
+ type = number
45
+ default = 768
46
+ }
47
+
48
+ variable "app_task_cpu" {
49
+ description = "The CPU units to allocate to the application task"
50
+ type = number
51
+ default = 256
52
+ }
53
+
54
+ variable "app_task_memory" {
55
+ description = "The memory to allocate to the application task"
56
+ type = number
57
+ default = 1024
58
+ }
59
+
60
+ variable "app_task_count" {
61
+ description = "The number of application tasks to run"
62
+ type = number
63
+ default = 1
64
+ }
65
+
66
+ variable "worker_task_cpu" {
67
+ description = "The CPU units to allocate to the worker task"
68
+ type = number
69
+ default = 256
70
+ }
71
+
72
+ variable "worker_task_memory" {
73
+ description = "The memory to allocate to the worker task"
74
+ type = number
75
+ default = 1024
76
+ }
77
+
78
+ variable "worker_task_count" {
79
+ description = "The number of worker tasks to run"
80
+ type = number
81
+ default = 1
82
+ }
83
+
84
+ variable "task_subnets" {
85
+ description = "The subnets to launch tasks in"
86
+ type = list
87
+ }
88
+
89
+ variable "vpc_id" {
90
+ description = "The VPC ID to launch things in"
91
+ type = string
92
+ }
93
+
94
+ variable "alb_listener" {
95
+ description = "The ALB Listener to use in the load balancer configuration"
96
+ }
97
+
98
+ variable "alb_target_group" {
99
+ description = "The ALB Target Group to use in the load balancer configuration"
100
+ }
101
+
102
+ variable "load_balance_security_group" {
103
+ description = "The Security group for the ALB"
104
+ }
105
+
106
+ variable "key_pair_public_key" {
107
+ description = "The public key for the EC2 Key Pair to use when running remote_console"
108
+ type = string
109
+ }
@@ -0,0 +1,62 @@
1
+ # WEB
2
+ resource "aws_cloudwatch_log_group" "web" {
3
+ name = "/ecs/service/${var.name}-web"
4
+ retention_in_days = "14"
5
+ }
6
+
7
+ resource "aws_cloudwatch_log_group" "app" {
8
+ name = "/ecs/service/${var.name}-app"
9
+ retention_in_days = "14"
10
+ }
11
+
12
+ data "template_file" "web_task_definition" {
13
+ template = file("${path.module}/task_definitions/web.json")
14
+
15
+ vars = {
16
+ app_log_path = aws_cloudwatch_log_group.app.name
17
+ web_log_path = aws_cloudwatch_log_group.web.name
18
+ app_image = "${var.app_repository_url}:latest"
19
+ web_image = "${var.web_repository_url}:latest"
20
+ environment = jsonencode(var.app_environment)
21
+ app_memory = var.app_process_memory
22
+ region = var.aws_region
23
+ }
24
+ }
25
+
26
+ resource "aws_ecs_task_definition" "web" {
27
+ family = "${var.name}-web"
28
+ network_mode = "awsvpc"
29
+ requires_compatibilities = ["FARGATE"]
30
+ cpu = var.app_task_cpu
31
+ memory = var.app_task_memory
32
+ execution_role_arn = aws_iam_role.ecs_execution_role.arn
33
+ task_role_arn = aws_iam_role.ecs_task_execution_role.arn
34
+
35
+ container_definitions = data.template_file.web_task_definition.rendered
36
+ }
37
+
38
+ resource "aws_ecs_service" "web" {
39
+ name = "${var.name}-web"
40
+ cluster = aws_ecs_cluster.main.id
41
+ task_definition = aws_ecs_task_definition.web.arn
42
+ desired_count = var.app_task_count
43
+ launch_type = "FARGATE"
44
+
45
+ network_configuration {
46
+ security_groups = [aws_security_group.ecs_tasks.id]
47
+ subnets = var.task_subnets
48
+ }
49
+
50
+ load_balancer {
51
+ target_group_arn = var.alb_target_group.id
52
+ container_name = "web"
53
+ container_port = "80"
54
+ }
55
+
56
+ depends_on = [var.alb_listener]
57
+
58
+ lifecycle {
59
+ create_before_destroy = true
60
+ ignore_changes = [task_definition]
61
+ }
62
+ }
@@ -0,0 +1,48 @@
1
+ # WORKER
2
+ resource "aws_cloudwatch_log_group" "worker" {
3
+ name = "/ecs/service/${var.name}-worker"
4
+ retention_in_days = "14"
5
+ }
6
+
7
+ data "template_file" "worker_task_definition" {
8
+ template = file("${path.module}/task_definitions/worker.json")
9
+
10
+ vars = {
11
+ app_log_path = aws_cloudwatch_log_group.worker.name
12
+ app_image = "${var.app_repository_url}:latest"
13
+ app_memory = var.app_process_memory
14
+ region = var.aws_region
15
+ worker_app_memory = var.worker_task_memory
16
+ environment = jsonencode(var.worker_environment)
17
+ }
18
+ }
19
+
20
+ resource "aws_ecs_task_definition" "worker" {
21
+ family = "${var.name}-worker"
22
+ network_mode = "awsvpc"
23
+ requires_compatibilities = ["FARGATE"]
24
+ cpu = var.worker_task_cpu
25
+ memory = var.worker_task_memory
26
+ execution_role_arn = aws_iam_role.ecs_execution_role.arn
27
+ task_role_arn = aws_iam_role.ecs_task_execution_role.arn
28
+
29
+ container_definitions = data.template_file.worker_task_definition.rendered
30
+ }
31
+
32
+ resource "aws_ecs_service" "worker" {
33
+ name = "${var.name}-worker"
34
+ cluster = aws_ecs_cluster.main.id
35
+ task_definition = aws_ecs_task_definition.worker.arn
36
+ desired_count = var.worker_task_count
37
+ launch_type = "FARGATE"
38
+
39
+ network_configuration {
40
+ security_groups = [aws_security_group.ecs_tasks.id]
41
+ subnets = var.task_subnets
42
+ }
43
+
44
+ lifecycle {
45
+ create_before_destroy = true
46
+ ignore_changes = [task_definition]
47
+ }
48
+ }
@@ -0,0 +1,19 @@
1
+ resource "aws_route53_zone" "primary" {
2
+ count = var.domain_name == null ? 0 : 1
3
+
4
+ name = var.domain_name
5
+ }
6
+
7
+ resource "aws_route53_record" "root" {
8
+ count = var.domain_name == null ? 0 : 1
9
+
10
+ zone_id = aws_route53_zone.primary[0].id
11
+ name = var.domain_name
12
+ type = "A"
13
+
14
+ alias {
15
+ name = module.application.alb.dns_name
16
+ zone_id = module.application.alb.zone_id
17
+ evaluate_target_health = true
18
+ }
19
+ }
@@ -0,0 +1,167 @@
1
+ provider "aws" {
2
+ version = "~> 2.8"
3
+ }
4
+
5
+ provider "template" {
6
+ version = "~> 2.0"
7
+ }
8
+
9
+ locals {
10
+ environment = "production"
11
+ ecr_expire_policy = <<EOF
12
+ {
13
+ "rules": [
14
+ {
15
+ "rulePriority": 1,
16
+ "description": "Expire images older than 14 days",
17
+ "selection": {
18
+ "tagStatus": "untagged",
19
+ "countType": "imageCountMoreThan",
20
+ "countNumber": 14
21
+ },
22
+ "action": {
23
+ "type": "expire"
24
+ }
25
+ }
26
+ ]
27
+ }
28
+ EOF
29
+ }
30
+
31
+ # You MUST run the TF files in the "shared" environment before this will work
32
+ terraform {
33
+ backend "s3" {
34
+ encrypt = true
35
+ bucket = "rails-app-terraform-production"
36
+ dynamodb_table = "rails-app-terraform-production"
37
+ region = "us-east-1"
38
+ key = "statefile"
39
+ }
40
+ }
41
+
42
+ module "vpc" {
43
+ source = "git://github.com/trobrock/terraform-vpc.git?ref=v1.0.0"
44
+
45
+ name = "${var.short_name}-${local.environment}"
46
+ }
47
+
48
+ resource "aws_ecr_repository" "app" {
49
+ name = "${var.short_name}-${local.environment}-app"
50
+ }
51
+
52
+ resource "aws_ecr_lifecycle_policy" "app" {
53
+ repository = aws_ecr_repository.app.name
54
+ policy = local.ecr_expire_policy
55
+ }
56
+
57
+ resource "aws_ecr_repository" "web" {
58
+ name = "${var.short_name}-${local.environment}-web"
59
+ }
60
+
61
+ resource "aws_ecr_lifecycle_policy" "web" {
62
+ repository = aws_ecr_repository.web.name
63
+ policy = local.ecr_expire_policy
64
+ }
65
+
66
+ module "code_pipeline" {
67
+ source = "git://github.com/trobrock/terraform-code-pipeline.git?ref=v2.0.0"
68
+
69
+ short_name = var.short_name
70
+ environment = local.environment
71
+ repo_owner = var.github_org
72
+ repo_name = var.github_repo
73
+
74
+ build_environment = [
75
+ {
76
+ name = "APP_REPOSITORY_URI"
77
+ value = aws_ecr_repository.app.repository_url
78
+ },
79
+ {
80
+ name = "WEB_REPOSITORY_URI"
81
+ value = aws_ecr_repository.web.repository_url
82
+ },
83
+ {
84
+ name = "DATABASE_URL"
85
+ value = module.database.url
86
+ },
87
+ {
88
+ name = "RAILS_ENV"
89
+ value = local.environment
90
+ },
91
+ {
92
+ name = "REDIS_URL"
93
+ value = module.redis.url
94
+ }
95
+ ]
96
+
97
+ lambda_subnet = module.vpc.private_subnets[0]
98
+ ecs_cluster = module.application.ecs_cluster
99
+ ecs_security_group_id = module.application.application_security_group.id
100
+ ecs_task_definition_family = module.application.one_off_task_definition_name
101
+ ecs_task_definition_name = "one_off"
102
+ deployments = [
103
+ {
104
+ name = "deploy-web"
105
+ service_name = module.application.web_service_name
106
+ file_name = "web_imagedefinitions.json"
107
+ },
108
+ {
109
+ name = "deploy-worker"
110
+ service_name = module.application.worker_service_name
111
+ file_name = "worker_imagedefinitions.json"
112
+ }
113
+ ]
114
+ }
115
+
116
+ module "database" {
117
+ source = "git://github.com/trobrock/terraform-database.git?ref=v1.0.1"
118
+
119
+ name = "${var.short_name}${local.environment}"
120
+ instance_class = "db.t2.small"
121
+ vpc_id = module.vpc.vpc_id
122
+ subnets = module.vpc.private_subnets
123
+ security_groups = [module.application.application_security_group.id]
124
+ username = var.short_name
125
+ password = "database20200207"
126
+ }
127
+
128
+ module "redis" {
129
+ source = "git://github.com/trobrock/terraform-redis.git?ref=v1.0.0"
130
+
131
+ name = "${var.name}-${local.environment}"
132
+ vpc_id = module.vpc.vpc_id
133
+ subnets = module.vpc.private_subnets
134
+ security_groups = [module.application.application_security_group.id]
135
+ }
136
+
137
+ module "application" {
138
+ source = "git://github.com/trobrock/terraform-rails-application.git?ref=v0.0.2"
139
+
140
+ name = "${var.short_name}-${local.environment}"
141
+ app_repository_url = aws_ecr_repository.app.repository_url
142
+ web_repository_url = aws_ecr_repository.web.repository_url
143
+ public_subnets = module.vpc.public_subnets
144
+ task_subnets = module.vpc.private_subnets
145
+ vpc_id = module.vpc.vpc_id
146
+ enable_ssl = var.enable_ssl
147
+ acm_certificate_arn = aws_acm_certificate.cert[0].arn
148
+ key_pair_public_key = var.ssh_public_key
149
+
150
+ app_environment = [
151
+ {
152
+ name = "DATABASE_URL"
153
+ value = module.database.url
154
+ },
155
+ {
156
+ name = "REDIS_URL"
157
+ value = module.redis.url
158
+ }
159
+ ]
160
+
161
+ worker_environment = [
162
+ {
163
+ name = "QUEUE"
164
+ value = "*"
165
+ }
166
+ ]
167
+ }
@@ -0,0 +1,28 @@
1
+ resource "aws_acm_certificate" "cert" {
2
+ count = var.enable_ssl ? 1 : 0
3
+
4
+ domain_name = var.domain_name
5
+ subject_alternative_names = ["*.${var.domain_name}"]
6
+ validation_method = "DNS"
7
+
8
+ lifecycle {
9
+ create_before_destroy = true
10
+ }
11
+ }
12
+
13
+ resource "aws_route53_record" "cert_validation" {
14
+ count = var.enable_ssl ? 1 : 0
15
+
16
+ name = aws_acm_certificate.cert[0].domain_validation_options[0].resource_record_name
17
+ type = aws_acm_certificate.cert[0].domain_validation_options[0].resource_record_type
18
+ zone_id = aws_route53_zone.primary[0].id
19
+ records = [aws_acm_certificate.cert[0].domain_validation_options[0].resource_record_value]
20
+ ttl = 60
21
+ }
22
+
23
+ resource "aws_acm_certificate_validation" "cert" {
24
+ count = var.enable_ssl ? 1 : 0
25
+
26
+ certificate_arn = aws_acm_certificate.cert[0].arn
27
+ validation_record_fqdns = [aws_route53_record.cert_validation[0].fqdn]
28
+ }
@@ -0,0 +1,36 @@
1
+ variable "name" {
2
+ description = "The long name to use on resources"
3
+ type = string
4
+ }
5
+
6
+ variable "short_name" {
7
+ description = "The short name to use on resources"
8
+ type = string
9
+ }
10
+
11
+ variable "github_org" {
12
+ description = "The name of the organization in GitHub"
13
+ type = string
14
+ }
15
+
16
+ variable "github_repo" {
17
+ description = "The name of the repository in GitHub"
18
+ type = string
19
+ }
20
+
21
+ variable "domain_name" {
22
+ description = "The primary domain name to launch the app on"
23
+ type = string
24
+ default = null
25
+ }
26
+
27
+ variable "enable_ssl" {
28
+ description = "Whether the app should be served over SSL"
29
+ type = bool
30
+ default = false
31
+ }
32
+
33
+ variable "ssh_public_key" {
34
+ description = "The public key for the SSH key to use in the SSHable group to access servers"
35
+ type = string
36
+ }
@@ -0,0 +1,41 @@
1
+ variable "name" {
2
+ type = string
3
+ description = "the name of the application"
4
+ }
5
+
6
+ provider "aws" {
7
+ version = "~> 2.31"
8
+ }
9
+
10
+ # PRODUCTION
11
+ resource "aws_s3_bucket" "production" {
12
+ bucket = "${var.name}-terraform-production"
13
+
14
+ versioning {
15
+ enabled = true
16
+ }
17
+
18
+ lifecycle {
19
+ prevent_destroy = true
20
+ }
21
+
22
+ tags = {
23
+ Name = "S3 Remote Terraform State Store for production"
24
+ }
25
+ }
26
+
27
+ resource "aws_dynamodb_table" "production" {
28
+ name = "${var.name}-terraform-production"
29
+ hash_key = "LockID"
30
+ read_capacity = 20
31
+ write_capacity = 20
32
+
33
+ attribute {
34
+ name = "LockID"
35
+ type = "S"
36
+ }
37
+
38
+ tags = {
39
+ Name = "DynamoDB Terraform State Lock Table for production"
40
+ }
41
+ }
@@ -0,0 +1,129 @@
1
+ {
2
+ "version": 4,
3
+ "terraform_version": "0.12.20",
4
+ "serial": 4,
5
+ "lineage": "96f07a4f-a32d-dbcd-f6e5-95a51e93647c",
6
+ "outputs": {},
7
+ "resources": [
8
+ {
9
+ "mode": "managed",
10
+ "type": "aws_dynamodb_table",
11
+ "name": "production",
12
+ "provider": "provider.aws",
13
+ "instances": [
14
+ {
15
+ "schema_version": 1,
16
+ "attributes": {
17
+ "arn": "arn:aws:dynamodb:us-east-1:947651631655:table/net-worth-monitor-terraform-production",
18
+ "attribute": [
19
+ {
20
+ "name": "LockID",
21
+ "type": "S"
22
+ }
23
+ ],
24
+ "billing_mode": "PROVISIONED",
25
+ "global_secondary_index": [],
26
+ "hash_key": "LockID",
27
+ "id": "net-worth-monitor-terraform-production",
28
+ "local_secondary_index": [],
29
+ "name": "net-worth-monitor-terraform-production",
30
+ "point_in_time_recovery": [
31
+ {
32
+ "enabled": false
33
+ }
34
+ ],
35
+ "range_key": null,
36
+ "read_capacity": 20,
37
+ "server_side_encryption": [],
38
+ "stream_arn": "",
39
+ "stream_enabled": false,
40
+ "stream_label": "",
41
+ "stream_view_type": "",
42
+ "tags": {
43
+ "Name": "DynamoDB Terraform State Lock Table for production"
44
+ },
45
+ "timeouts": null,
46
+ "ttl": [
47
+ {
48
+ "attribute_name": "",
49
+ "enabled": false
50
+ }
51
+ ],
52
+ "write_capacity": 20
53
+ },
54
+ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJ1cGRhdGUiOjM2MDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "mode": "managed",
60
+ "type": "aws_kms_key",
61
+ "name": "parameter_store",
62
+ "provider": "provider.aws",
63
+ "instances": [
64
+ {
65
+ "schema_version": 0,
66
+ "attributes": {
67
+ "arn": "arn:aws:kms:us-east-1:947651631655:key/d9e991ab-6627-4b87-8377-88d9b44eda0a",
68
+ "customer_master_key_spec": "SYMMETRIC_DEFAULT",
69
+ "deletion_window_in_days": 10,
70
+ "description": "Parameter store kms master key (Chamber CLI)",
71
+ "enable_key_rotation": true,
72
+ "id": "d9e991ab-6627-4b87-8377-88d9b44eda0a",
73
+ "is_enabled": true,
74
+ "key_id": "d9e991ab-6627-4b87-8377-88d9b44eda0a",
75
+ "key_usage": "ENCRYPT_DECRYPT",
76
+ "policy": "{\"Id\":\"key-default-1\",\"Statement\":[{\"Action\":\"kms:*\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::947651631655:root\"},\"Resource\":\"*\",\"Sid\":\"Enable IAM User Permissions\"}],\"Version\":\"2012-10-17\"}",
77
+ "tags": null
78
+ },
79
+ "private": "bnVsbA=="
80
+ }
81
+ ]
82
+ },
83
+ {
84
+ "mode": "managed",
85
+ "type": "aws_s3_bucket",
86
+ "name": "production",
87
+ "provider": "provider.aws",
88
+ "instances": [
89
+ {
90
+ "schema_version": 0,
91
+ "attributes": {
92
+ "acceleration_status": "",
93
+ "acl": "private",
94
+ "arn": "arn:aws:s3:::net-worth-monitor-terraform-production",
95
+ "bucket": "net-worth-monitor-terraform-production",
96
+ "bucket_domain_name": "net-worth-monitor-terraform-production.s3.amazonaws.com",
97
+ "bucket_prefix": null,
98
+ "bucket_regional_domain_name": "net-worth-monitor-terraform-production.s3.amazonaws.com",
99
+ "cors_rule": [],
100
+ "force_destroy": false,
101
+ "hosted_zone_id": "Z3AQBSTGFYJSTF",
102
+ "id": "net-worth-monitor-terraform-production",
103
+ "lifecycle_rule": [],
104
+ "logging": [],
105
+ "object_lock_configuration": [],
106
+ "policy": null,
107
+ "region": "us-east-1",
108
+ "replication_configuration": [],
109
+ "request_payer": "BucketOwner",
110
+ "server_side_encryption_configuration": [],
111
+ "tags": {
112
+ "Name": "S3 Remote Terraform State Store for production"
113
+ },
114
+ "versioning": [
115
+ {
116
+ "enabled": true,
117
+ "mfa_delete": false
118
+ }
119
+ ],
120
+ "website": [],
121
+ "website_domain": null,
122
+ "website_endpoint": null
123
+ },
124
+ "private": "bnVsbA=="
125
+ }
126
+ ]
127
+ }
128
+ ]
129
+ }
@@ -0,0 +1,4 @@
1
+ name = "<%= app_config.slugified_name %>"
2
+ short_name = "<%= app_config.short_name %>"
3
+ github_org = "<%= app_config.github_org %>"
4
+ github_repo = "<%= app_config.github_repo %>"
@@ -0,0 +1 @@
1
+ name = "<%= app_config.slugified_name %>"