Skip to content

Commit

Permalink
fix: port iam_users from postgresql module (#467)
Browse files Browse the repository at this point in the history
Signed-off-by: John Jelinek <john.jelinek@liveramp.com>
Co-authored-by: Awais Malik <awmalik@google.com>
  • Loading branch information
johnjelinek and g-awmalik committed May 6, 2023
1 parent 2187bdc commit 5732125
Show file tree
Hide file tree
Showing 16 changed files with 124 additions and 7 deletions.
1 change: 1 addition & 0 deletions examples/mysql-private/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ terraform destroy

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| cloudsql\_mysql\_sa | IAM service account user created for Cloud SQL. | `string` | n/a | yes |
| db\_name | The name of the SQL Database instance | `string` | `"example-mysql-private"` | no |
| network\_name | n/a | `string` | `"mysql-private"` | no |
| project\_id | The project to run tests against | `string` | n/a | yes |
Expand Down
21 changes: 20 additions & 1 deletion examples/mysql-private/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,18 @@ module "safer-mysql-db" {

deletion_protection = false

database_version = "MYSQL_5_6"
database_version = "MYSQL_8_0"
region = "us-central1"
zone = "us-central1-c"
tier = "db-n1-standard-1"

database_flags = [
{
name = "cloudsql_iam_authentication"
value = "on"
},
]

// By default, all users will be permitted to connect only via the
// Cloud SQL proxy.
additional_users = [
Expand All @@ -75,6 +82,18 @@ module "safer-mysql-db" {
},
]

# Supports creation of both IAM Users and IAM Service Accounts with provided emails
iam_users = [
{
id = "cloudsql_mysql_sa",
email = var.cloudsql_mysql_sa
},
{
id = "dbadmin",
email = "dbadmin@goosecorp.org"
}
]

assign_public_ip = "true"
vpc_network = module.network-safer-mysql-simple.network_self_link
allocated_ip_range = module.private-service-access.google_compute_global_address_name
Expand Down
4 changes: 4 additions & 0 deletions examples/mysql-private/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ variable "db_name" {
default = "example-mysql-private"
}

variable "cloudsql_mysql_sa" {
type = string
description = "IAM service account user created for Cloud SQL."
}
3 changes: 3 additions & 0 deletions modules/mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Note: CloudSQL provides [disk autoresize](https://cloud.google.com/sql/docs/mysq
| enable\_random\_password\_special | Enable special characters in generated random passwords. | `bool` | `false` | no |
| encryption\_key\_name | The full path to the encryption key used for the CMEK disk encryption | `string` | `null` | no |
| follow\_gae\_application | A Google App Engine application whose zone to remain in. Must be in the same region as this instance. | `string` | `null` | no |
| iam\_users | A list of IAM users to be created in your CloudSQL instance | <pre>list(object({<br> id = string,<br> email = string<br> }))</pre> | `[]` | no |
| insights\_config | The insights\_config settings for the database. | <pre>object({<br> query_string_length = number<br> record_application_tags = bool<br> record_client_address = bool<br> })</pre> | `null` | no |
| ip\_configuration | The ip\_configuration settings subblock | <pre>object({<br> authorized_networks = list(map(string))<br> ipv4_enabled = bool<br> private_network = string<br> require_ssl = bool<br> allocated_ip_range = string<br> enable_private_path_for_google_cloud_services = optional(bool)<br> })</pre> | <pre>{<br> "allocated_ip_range": null,<br> "authorized_networks": [],<br> "enable_private_path_for_google_cloud_services": false,<br> "ipv4_enabled": true,<br> "private_network": null,<br> "require_ssl": null<br>}</pre> | no |
| maintenance\_window\_day | The day of week (1-7) for the master instance maintenance. | `number` | `1` | no |
Expand All @@ -53,6 +54,7 @@ Note: CloudSQL provides [disk autoresize](https://cloud.google.com/sql/docs/mysq
| secondary\_zone | The preferred zone for the secondary/failover instance, it should be something like: `us-central1-a`, `us-east1-c`. | `string` | `null` | no |
| tier | The tier for the master instance. | `string` | `"db-n1-standard-1"` | no |
| update\_timeout | The optional timout that is applied to limit long database updates. | `string` | `"30m"` | no |
| user\_deletion\_policy | The deletion policy for the user. Setting ABANDON allows the resource to be abandoned rather than deleted. This is useful for Postgres, where users cannot be deleted from the API if they have been granted SQL roles. Possible values are: "ABANDON". | `string` | `null` | no |
| user\_host | The host for the default user | `string` | `"%"` | no |
| user\_labels | The key/value labels for the master instances. | `map(string)` | `{}` | no |
| user\_name | The name of the default user | `string` | `"default"` | no |
Expand All @@ -65,6 +67,7 @@ Note: CloudSQL provides [disk autoresize](https://cloud.google.com/sql/docs/mysq
|------|-------------|
| additional\_users | List of maps of additional users and passwords |
| generated\_user\_password | The auto generated default user password if not input password was provided |
| iam\_users | The list of the IAM users with access to the CloudSQL instance |
| instance\_connection\_name | The connection name of the master instance to be used in connection strings |
| instance\_first\_ip\_address | The first IPv4 address of the addresses assigned for the master instance. |
| instance\_ip\_address | The IPv4 address assigned for the master instance |
Expand Down
20 changes: 20 additions & 0 deletions modules/mysql/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ locals {

databases = { for db in var.additional_databases : db.name => db }
users = { for u in var.additional_users : u.name => u }
iam_users = {
for user in var.iam_users : user.id => {
email = user.email,
is_account_sa = trimsuffix(user.email, "gserviceaccount.com") == user.email ? false : true
}
}

// HA method using REGIONAL availability_type requires binary logs to be enabled
binary_log_enabled = var.availability_type == "REGIONAL" ? true : lookup(var.backup_configuration, "binary_log_enabled", null)
Expand Down Expand Up @@ -258,6 +264,20 @@ resource "google_sql_user" "additional_users" {
]
}

resource "google_sql_user" "iam_account" {
for_each = local.iam_users

project = var.project_id
name = each.value.email
instance = google_sql_database_instance.default.name
type = each.value.is_account_sa ? "CLOUD_IAM_SERVICE_ACCOUNT" : "CLOUD_IAM_USER"

depends_on = [
null_resource.module_depends_on,
]
deletion_policy = var.user_deletion_policy
}

resource "null_resource" "module_depends_on" {
triggers = {
value = length(var.module_depends_on)
Expand Down
9 changes: 9 additions & 0 deletions modules/mysql/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ spec:
record_application_tags = bool
record_client_address = bool
})
- name: iam_users
description: A list of IAM users to be created in your CloudSQL instance
type: |-
list(object({
id = string
email = string
}))
- name: ip_configuration
description: The ip_configuration settings subblock
type: |-
Expand Down Expand Up @@ -338,6 +345,8 @@ spec:
description: List of maps of additional users and passwords
- name: generated_user_password
description: The auto generated default user password if not input password was provided
- name: iam_users
description: The list of the IAM users with access to the CloudSQL instance
- name: instance_connection_name
description: The connection name of the master instance to be used in connection strings
- name: instance_first_ip_address
Expand Down
5 changes: 5 additions & 0 deletions modules/mysql/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ output "private_ip_address" {
value = google_sql_database_instance.default.private_ip_address
}

output "iam_users" {
description = "The list of the IAM users with access to the CloudSQL instance"
value = var.iam_users
}

// Resources
output "primary" {
value = google_sql_database_instance.default
Expand Down
15 changes: 15 additions & 0 deletions modules/mysql/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ variable "additional_users" {
}
}

variable "iam_users" {
description = "A list of IAM users to be created in your CloudSQL instance"
type = list(object({
id = string,
email = string
}))
default = []
}

variable "create_timeout" {
description = "The optional timout that is applied to limit long database creates."
type = string
Expand Down Expand Up @@ -408,3 +417,9 @@ variable "connector_enforcement" {
type = bool
default = false
}

variable "user_deletion_policy" {
description = "The deletion policy for the user. Setting ABANDON allows the resource to be abandoned rather than deleted. This is useful for Postgres, where users cannot be deleted from the API if they have been granted SQL roles. Possible values are: \"ABANDON\"."
type = string
default = null
}
1 change: 1 addition & 0 deletions modules/safer_mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ mysql -S $HOME/mysql_sockets/myproject:region:instance -u user -p
| disk\_type | The disk type for the master instance. | `string` | `"PD_SSD"` | no |
| encryption\_key\_name | The full path to the encryption key used for the CMEK disk encryption | `string` | `null` | no |
| follow\_gae\_application | A Google App Engine application whose zone to remain in. Must be in the same region as this instance. | `string` | `null` | no |
| iam\_users | A list of IAM users to be created in your CloudSQL instance | <pre>list(object({<br> id = string,<br> email = string<br> }))</pre> | `[]` | no |
| insights\_config | The insights\_config settings for the database. | <pre>object({<br> query_string_length = number<br> record_application_tags = bool<br> record_client_address = bool<br> })</pre> | `null` | no |
| maintenance\_window\_day | The day of week (1-7) for the master instance maintenance. | `number` | `1` | no |
| maintenance\_window\_hour | The hour of day (0-23) maintenance window for the master instance maintenance. | `number` | `23` | no |
Expand Down
1 change: 1 addition & 0 deletions modules/safer_mysql/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ module "safer_mysql" {

user_password = var.user_password
additional_users = var.additional_users
iam_users = var.iam_users

// Read replica
read_replica_name_suffix = var.read_replica_name_suffix
Expand Down
9 changes: 9 additions & 0 deletions modules/safer_mysql/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,15 @@ variable "additional_users" {
default = []
}

variable "iam_users" {
description = "A list of IAM users to be created in your CloudSQL instance"
type = list(object({
id = string,
email = string
}))
default = []
}

variable "create_timeout" {
description = "The optional timout that is applied to limit long database creates."
type = string
Expand Down
8 changes: 4 additions & 4 deletions test/fixtures/mysql-private/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/

module "example" {
source = "../../../examples/mysql-private"
project_id = var.project_id
network_name = var.network_name
source = "../../../examples/mysql-private"
project_id = var.project_id
network_name = var.network_name
cloudsql_mysql_sa = var.cloudsql_mysql_sa
}

5 changes: 5 additions & 0 deletions test/fixtures/mysql-private/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ variable "network_name" {
default = "mysql-private"
type = string
}

variable "cloudsql_mysql_sa" {
type = string
description = "IAM service account user created for Cloud SQL for MySql."
}
17 changes: 16 additions & 1 deletion test/integration/mysql-private/mysql_private_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestMySqlPrivateModule(t *testing.T) {
assert.Equal("stable", op.Get("settings.maintenanceWindow.updateTrack").String(), "Expected stable maintenanceWindow.updateTrack")

// assert standard database settings
assert.Equal("MYSQL_5_6", op.Get("databaseVersion").String(), "Expected MYSQL_5_6 databaseVersion")
assert.Equal("MYSQL_8_0", op.Get("databaseVersion").String(), "Expected MYSQL_5_6 databaseVersion")
assert.Equal("SECOND_GEN", op.Get("backendType").String(), "Expected SECOND_GEN backendType")
assert.Equal("RUNNABLE", op.Get("state").String(), "Expected RUNNABLE state")
assert.Equal("us-central1", op.Get("region").String(), "Expected us-central1 region")
Expand All @@ -69,6 +69,21 @@ func TestMySqlPrivateModule(t *testing.T) {

op = gcloud.Run(t, fmt.Sprintf("compute addresses list --global --filter='%s' --project %s", mySql.GetStringOutput("reserved_range_name"), mySql.GetStringOutput("project_id")))
assert.Equal(1, len(op.Array()), "Expected one peering setup")

// assert users
op = gcloud.Run(t, fmt.Sprintf("sql users list --instance %s --project %s", mySql.GetStringOutput("name"), mySql.GetStringOutput("project_id")))
containsIamUser := false
containsIamSa := false
for _, element := range op.Array() {
if element.Get("type").String() == "CLOUD_IAM_USER" && element.Get("name").String() == "dbadmin" {
containsIamUser = true
}
if element.Get("type").String() == "CLOUD_IAM_SERVICE_ACCOUNT" && element.Get("name").String() == "cloudsql-mysql-sa-01" {
containsIamSa = true
}
}
assert.Truef(containsIamUser, "Expected %s cloud iam user", "dbadmin")
assert.Truef(containsIamSa, "Expected cloud iam sa [%s]", "cloudsql-mysql-sa-01")
})

mySql.Test()
Expand Down
5 changes: 5 additions & 0 deletions test/setup/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ resource "google_service_account" "cloudsql_pg_sa" {
project = module.project.project_id
account_id = "cloudsql-pg-sa-01"
}

resource "google_service_account" "cloudsql_mysql_sa" {
project = module.project.project_id
account_id = "cloudsql-mysql-sa-01"
}
7 changes: 6 additions & 1 deletion test/setup/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ output "sa_key" {

output "cloudsql_pg_sa" {
value = google_service_account.cloudsql_pg_sa.email
description = "IAM service account user created for Cloud SQL."
description = "IAM service account user created for Cloud SQL for PostgreSQL."
}

output "cloudsql_mysql_sa" {
value = google_service_account.cloudsql_mysql_sa.email
description = "IAM service account user created for Cloud SQL for MySql."
}

0 comments on commit 5732125

Please sign in to comment.