Skip to content

Commit 09a476c

Browse files
authored
feat(deploy-role): create AWS IAM role for deployment (#41)
* feat(deploy-role): create AWS IAM role for deployment
1 parent 236bc02 commit 09a476c

File tree

7 files changed

+96
-6
lines changed

7 files changed

+96
-6
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,22 @@ module "static-site" {
8787
|------|---------|
8888
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5, < 2.0 |
8989
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.27 |
90+
| <a name="requirement_gitlab"></a> [gitlab](#requirement\_gitlab) | >= 15.7, < 18.0 |
9091

9192
## Providers
9293

9394
| Name | Version |
9495
|------|---------|
9596
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.61.0 |
97+
| <a name="provider_gitlab"></a> [gitlab](#provider\_gitlab) | 17.2.0 |
9698

9799
## Modules
98100

99101
| Name | Source | Version |
100102
|------|--------|---------|
101103
| <a name="module_certificate"></a> [certificate](#module\_certificate) | terraform-aws-modules/acm/aws | 5.1.1 |
102104
| <a name="module_gitlab"></a> [gitlab](#module\_gitlab) | ./modules/gitlab | n/a |
103-
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.2.2 |
105+
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.6.1 |
104106

105107
## Resources
106108

@@ -111,6 +113,8 @@ module "static-site" {
111113
| [aws_cloudfront_origin_access_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource |
112114
| [aws_cloudfront_response_headers_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_response_headers_policy) | resource |
113115
| [aws_iam_access_key.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
116+
| [aws_iam_role.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
117+
| [aws_iam_role_policy.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
114118
| [aws_iam_user.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
115119
| [aws_iam_user_policy.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
116120
| [aws_kms_alias.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
@@ -121,20 +125,25 @@ module "static-site" {
121125
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
122126
| [aws_cloudfront_cache_policy.managed_caching_disabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_cache_policy) | data source |
123127
| [aws_cloudfront_origin_request_policy.managed_all_viewer_and_cloudfront_headers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_origin_request_policy) | data source |
128+
| [aws_iam_openid_connect_provider.gitlab](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_openid_connect_provider) | data source |
129+
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
124130
| [aws_iam_policy_document.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
125131
| [aws_iam_policy_document.kms_key_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
126132
| [aws_iam_policy_document.s3_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
127133
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
134+
| [gitlab_project.this](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/data-sources/project) | data source |
128135

129136
## Inputs
130137

131138
| Name | Description | Type | Default | Required |
132139
|------|-------------|------|---------|:--------:|
133140
| <a name="input_aws_env_vars_suffix"></a> [aws\_env\_vars\_suffix](#input\_aws\_env\_vars\_suffix) | Append suffix for Gitlab CI/CD environment variables if needed | `string` | `""` | no |
134141
| <a name="input_cloudfront_price_class"></a> [cloudfront\_price\_class](#input\_cloudfront\_price\_class) | CloudFront price class | `string` | `"PriceClass_100"` | no |
142+
| <a name="input_custom_headers"></a> [custom\_headers](#input\_custom\_headers) | n/a | <pre>object({<br/> headers = optional(map(object({<br/> override = optional(bool, true)<br/> value = string<br/> })))<br/> cors_rules = optional(object({<br/> use = optional(bool, false)<br/> allowed_headers = optional(list(string))<br/> allowed_methods = optional(list(string))<br/> allowed_origins = optional(list(string))<br/> expose_headers = optional(list(string))<br/> max_age_seconds = optional(number)<br/> override = optional(bool, true)<br/> }), null)<br/> frame_options = optional(object({<br/> use = optional(bool, false)<br/> frame_option = string<br/> override = optional(bool, true)<br/> }), null)<br/> referrer_policy = optional(object({<br/> use = optional(bool, false)<br/> referrer_policy = string<br/> override = optional(bool, true)<br/> }), null)<br/> xss_protection = optional(object({<br/> use = optional(bool, false)<br/> mode_block = bool<br/> protection = bool<br/> override = optional(bool, true)<br/> }), null)<br/> content_security_policy = optional(object({<br/> use = optional(bool, false)<br/> content_security_policy = string<br/> override = optional(bool, true)<br/> }), null)<br/> strict_transport_security = optional(object({<br/> use = optional(bool, false)<br/> access_control_max_age_sec = string<br/> include_subdomains = bool<br/> preload = bool<br/> override = optional(bool, true)<br/> }), null)<br/> content_type_options = optional(object({<br/> override = optional(bool, true)<br/> }), null)<br/> })</pre> | `null` | no |
135143
| <a name="input_default_ttl"></a> [default\_ttl](#input\_default\_ttl) | Default amount of time that you want objects to stay in a CloudFront cache | `number` | `3600` | no |
136144
| <a name="input_domain_zone_id"></a> [domain\_zone\_id](#input\_domain\_zone\_id) | The ID of the hosted zone for domain | `string` | n/a | yes |
137145
| <a name="input_domains"></a> [domains](#input\_domains) | List of domain aliases. You can also specify wildcard eg.: `*.example.com` | `list(string)` | n/a | yes |
146+
| <a name="input_enable_deploy_role"></a> [enable\_deploy\_role](#input\_enable\_deploy\_role) | Toggle IAM role creation for S3 deploy & CloudFront invalidation; This requires existing aws\_iam\_openid\_connect\_provider matching domain of your gitlab provider | `bool` | `false` | no |
138147
| <a name="input_enable_deploy_user"></a> [enable\_deploy\_user](#input\_enable\_deploy\_user) | Toggle s3 deploy user creation | `bool` | `true` | no |
139148
| <a name="input_encrypt_with_kms"></a> [encrypt\_with\_kms](#input\_encrypt\_with\_kms) | Enable server side s3 bucket encryption with KMS key | `bool` | `false` | no |
140149
| <a name="input_extra_domains"></a> [extra\_domains](#input\_extra\_domains) | Map of extra\_domains with domain name and zone\_id | `map(string)` | `{}` | no |
@@ -154,10 +163,13 @@ module "static-site" {
154163
| <a name="input_proxy_paths"></a> [proxy\_paths](#input\_proxy\_paths) | n/a | <pre>list(object({<br/> origin_domain = string<br/> path_prefix = string<br/> }))</pre> | `[]` | no |
155164
| <a name="input_response_header_access_control_allow_credentials"></a> [response\_header\_access\_control\_allow\_credentials](#input\_response\_header\_access\_control\_allow\_credentials) | n/a | `bool` | `false` | no |
156165
| <a name="input_response_header_origin_override"></a> [response\_header\_origin\_override](#input\_response\_header\_origin\_override) | n/a | `bool` | `false` | no |
166+
| <a name="input_restriction_type"></a> [restriction\_type](#input\_restriction\_type) | Apply for geo restrictions, values: none, whitelist, blacklist | `string` | `"none"` | no |
167+
| <a name="input_restrictions_locations"></a> [restrictions\_locations](#input\_restrictions\_locations) | List of country codes | `list(string)` | `null` | no |
157168
| <a name="input_s3_bucket_name"></a> [s3\_bucket\_name](#input\_s3\_bucket\_name) | n/a | `string` | n/a | yes |
158169
| <a name="input_s3_bucket_policy"></a> [s3\_bucket\_policy](#input\_s3\_bucket\_policy) | Additional S3 bucket policy | `string` | `"{}"` | no |
159170
| <a name="input_s3_cors_rule"></a> [s3\_cors\_rule](#input\_s3\_cors\_rule) | List of maps containing rules for Cross-Origin Resource Sharing. | <pre>list(object({<br/> allowed_headers = optional(list(string))<br/> allowed_methods = optional(list(string))<br/> allowed_origins = optional(list(string))<br/> expose_headers = optional(list(string))<br/> max_age_seconds = optional(number)<br/> }))</pre> | `[]` | no |
160171
| <a name="input_tags"></a> [tags](#input\_tags) | n/a | `map(string)` | `{}` | no |
172+
| <a name="input_waf_acl_arn"></a> [waf\_acl\_arn](#input\_waf\_acl\_arn) | WAF ACL ARN | `string` | `null` | no |
161173

162174
## Outputs
163175

deploy.tf

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,65 @@
11
locals {
2-
gitlab_project_ids = toset(concat(var.gitlab_project_ids, var.gitlab_project_id != "" ? [var.gitlab_project_id] : []))
2+
gitlab_project_ids = toset(concat(var.gitlab_project_ids, var.gitlab_project_id != "" ? [var.gitlab_project_id] : []))
3+
first_project_web_url = data.gitlab_project.this[element(keys(data.gitlab_project.this), 0)].web_url
4+
gitlab_domain = regex("https://([^/]+)/.*", local.first_project_web_url)[0]
5+
}
6+
7+
data "gitlab_project" "this" {
8+
for_each = local.gitlab_project_ids
9+
id = each.value
10+
}
11+
12+
data "aws_iam_openid_connect_provider" "gitlab" {
13+
count = var.enable_deploy_role ? 1 : 0
14+
url = format("https://%s", local.gitlab_domain)
15+
}
16+
17+
data "aws_iam_policy_document" "assume_role" {
18+
count = var.enable_deploy_role ? 1 : 0
19+
statement {
20+
actions = ["sts:AssumeRoleWithWebIdentity"]
21+
effect = "Allow"
22+
23+
condition {
24+
test = "ForAnyValue:StringLike"
25+
values = [for repo in local.gitlab_project_ids : format("project_path:%s:ref_type:*:ref:*", data.gitlab_project.this[repo].path_with_namespace)]
26+
variable = format("%s:sub", local.gitlab_domain)
27+
}
28+
29+
principals {
30+
identifiers = [data.aws_iam_openid_connect_provider.gitlab[0].arn]
31+
type = "Federated"
32+
}
33+
}
34+
}
35+
36+
resource "aws_iam_role" "deploy" {
37+
count = var.enable_deploy_role ? 1 : 0
38+
assume_role_policy = data.aws_iam_policy_document.assume_role[0].json
39+
description = format("Role used by the GitLab project %s", "GITLAB_PROJECT_PLACEHOLDER")
40+
name = "zvirt-${local.main_domain_sanitized}-deploy"
41+
tags = var.tags
42+
}
43+
44+
resource "aws_iam_role_policy" "deploy" {
45+
count = var.enable_deploy_role ? 1 : 0
46+
name = "S3Deploy-CFInvalidate"
47+
role = aws_iam_role.deploy[0].id
48+
policy = data.aws_iam_policy_document.deploy[0].json
349
}
450

551
resource "aws_iam_user" "deploy" {
6-
count = var.enable_deploy_user == true ? 1 : 0
52+
count = var.enable_deploy_user ? 1 : 0
753
name = "zvirt-${local.main_domain_sanitized}-deploy"
854
}
955

1056
resource "aws_iam_access_key" "deploy" {
11-
count = var.enable_deploy_user == true ? 1 : 0
57+
count = var.enable_deploy_user ? 1 : 0
1258
user = aws_iam_user.deploy[0].name
1359
}
1460

1561
data "aws_iam_policy_document" "deploy" {
16-
count = var.enable_deploy_user == true ? 1 : 0
62+
count = var.enable_deploy_user || var.enable_deploy_role ? 1 : 0
1763
statement {
1864
effect = "Allow"
1965
actions = [
@@ -38,7 +84,7 @@ data "aws_iam_policy_document" "deploy" {
3884
}
3985

4086
resource "aws_iam_user_policy" "deploy" {
41-
count = var.enable_deploy_user == true ? 1 : 0
87+
count = var.enable_deploy_user ? 1 : 0
4288

4389
user = aws_iam_user.deploy[0].name
4490

@@ -55,6 +101,7 @@ module "gitlab" {
55101

56102
aws_s3_bucket_name = module.s3_bucket.s3_bucket_id
57103
aws_cloudfront_distribution_id = aws_cloudfront_distribution.this.id
104+
aws_role_arn = aws_iam_role.deploy[0].arn
58105
aws_access_key_id = aws_iam_access_key.deploy[0].id
59106
aws_secret_access_key = aws_iam_access_key.deploy[0].secret
60107
aws_default_region = data.aws_region.current.name

modules/gitlab/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ No modules.
7070
| [gitlab_project_variable.cloudfront_distribution_id](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
7171
| [gitlab_project_variable.s3_bucket](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
7272
| [gitlab_project_variable.site_aws_access_key_id](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
73+
| [gitlab_project_variable.site_aws_role_arn](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
7374
| [gitlab_project_variable.site_aws_secret_access_key](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
7475
| [gitlab_project.this](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/data-sources/project) | data source |
7576

@@ -81,6 +82,7 @@ No modules.
8182
| <a name="input_aws_cloudfront_distribution_id"></a> [aws\_cloudfront\_distribution\_id](#input\_aws\_cloudfront\_distribution\_id) | n/a | `string` | n/a | yes |
8283
| <a name="input_aws_default_region"></a> [aws\_default\_region](#input\_aws\_default\_region) | n/a | `string` | n/a | yes |
8384
| <a name="input_aws_env_vars_suffix"></a> [aws\_env\_vars\_suffix](#input\_aws\_env\_vars\_suffix) | n/a | `string` | `""` | no |
85+
| <a name="input_aws_role_arn"></a> [aws\_role\_arn](#input\_aws\_role\_arn) | n/a | `string` | n/a | yes |
8486
| <a name="input_aws_s3_bucket_name"></a> [aws\_s3\_bucket\_name](#input\_aws\_s3\_bucket\_name) | n/a | `string` | n/a | yes |
8587
| <a name="input_aws_secret_access_key"></a> [aws\_secret\_access\_key](#input\_aws\_secret\_access\_key) | n/a | `string` | n/a | yes |
8688
| <a name="input_gitlab_environment"></a> [gitlab\_environment](#input\_gitlab\_environment) | n/a | `string` | `"*"` | no |

modules/gitlab/main.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ resource "gitlab_project_variable" "cloudfront_distribution_id" {
4848
environment_scope = var.gitlab_environment
4949
}
5050

51+
resource "gitlab_project_variable" "site_aws_role_arn" {
52+
for_each = data.gitlab_project.this
53+
54+
project = each.value.id
55+
56+
protected = false
57+
masked = false
58+
raw = true
59+
60+
key = "AWS_ROLE_ARN${var.aws_env_vars_suffix}"
61+
value = var.aws_role_arn
62+
63+
environment_scope = var.gitlab_environment
64+
}
65+
5166
resource "gitlab_project_variable" "site_aws_access_key_id" {
5267
for_each = data.gitlab_project.this
5368

modules/gitlab/variables.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ variable "aws_cloudfront_distribution_id" {
1515
type = string
1616
}
1717

18+
variable "aws_role_arn" {
19+
type = string
20+
}
21+
1822
variable "aws_access_key_id" {
1923
type = string
2024
}

variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ variable "functions" {
108108
default = {}
109109
}
110110

111+
variable "enable_deploy_role" {
112+
type = bool
113+
default = false
114+
description = "Toggle IAM role creation for S3 deploy & CloudFront invalidation; This requires existing aws_iam_openid_connect_provider matching domain of your gitlab provider"
115+
}
116+
111117
variable "enable_deploy_user" {
112118
type = bool
113119
default = true

versions.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ terraform {
77
version = "~> 5.27"
88
configuration_aliases = [aws.us_east_1]
99
}
10+
gitlab = {
11+
source = "gitlabhq/gitlab"
12+
version = ">= 15.7, < 18.0"
13+
}
1014
}
1115
}

0 commit comments

Comments
 (0)