Skip to content

Commit

Permalink
feat: Add support for relational database datasources (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
carldjohnston authored Nov 1, 2023
1 parent 8f658e4 commit 87ff09b
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 42 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ module "appsync" {
}
eventbridge1 = {
type = "AMAZON_EVENTBRIDGE"
event_bus_arn = "aws:arn:events:us-west-1:135367859850:event-bus/eventbridge1"
type = "AMAZON_EVENTBRIDGE"
event_bus_arn = "arn:aws:events:us-west-1:135367859850:event-bus/eventbridge1"
}
rds1 = {
type = "RELATIONAL_DATABASE"
cluster_arn = "arn:aws:rds:us-west-1:135367859850:cluster:rds1"
secret_arn = "arn:aws:secretsmanager:us-west-1:135367859850:secret:rds-secret1"
database_name = "mydb"
schema = "myschema"
}
}
Expand Down Expand Up @@ -198,9 +206,11 @@ No modules.
| <a name="input_name"></a> [name](#input\_name) | Name of GraphQL API | `string` | `""` | no |
| <a name="input_openid_connect_config"></a> [openid\_connect\_config](#input\_openid\_connect\_config) | Nested argument containing OpenID Connect configuration. | `map(string)` | `{}` | no |
| <a name="input_opensearchservice_allowed_actions"></a> [opensearchservice\_allowed\_actions](#input\_opensearchservice\_allowed\_actions) | List of allowed IAM actions for datasources type AMAZON\_OPENSEARCH\_SERVICE | `list(string)` | <pre>[<br> "es:ESHttpDelete",<br> "es:ESHttpHead",<br> "es:ESHttpGet",<br> "es:ESHttpPost",<br> "es:ESHttpPut"<br>]</pre> | no |
| <a name="input_relational_database_allowed_actions"></a> [relational\_database\_allowed\_actions](#input\_relational\_database\_allowed\_actions) | List of allowed IAM actions for datasources type RELATIONAL\_DATABASE | `list(string)` | <pre>[<br> "rds-data:BatchExecuteStatement",<br> "rds-data:BeginTransaction",<br> "rds-data:CommitTransaction",<br> "rds-data:ExecuteStatement",<br> "rds-data:RollbackTransaction"<br>]</pre> | no |
| <a name="input_resolver_caching_ttl"></a> [resolver\_caching\_ttl](#input\_resolver\_caching\_ttl) | Default caching TTL for resolvers when caching is enabled | `number` | `60` | no |
| <a name="input_resolvers"></a> [resolvers](#input\_resolvers) | Map of resolvers to create | `any` | `{}` | no |
| <a name="input_schema"></a> [schema](#input\_schema) | The schema definition, in GraphQL schema language format. Terraform cannot perform drift detection of this configuration. | `string` | `""` | no |
| <a name="input_secrets_manager_allowed_actions"></a> [secrets\_manager\_allowed\_actions](#input\_secrets\_manager\_allowed\_actions) | List of allowed IAM actions for secrets manager datasources type RELATIONAL\_DATABASE | `list(string)` | <pre>[<br> "secretsmanager:GetSecretValue"<br>]</pre> | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Map of tags to add to all GraphQL resources created by this module | `map(string)` | `{}` | no |
| <a name="input_user_pool_config"></a> [user\_pool\_config](#input\_user\_pool\_config) | The Amazon Cognito User Pool configuration. | `map(string)` | `{}` | no |
| <a name="input_visibility"></a> [visibility](#input\_visibility) | The API visibility. Valid values: GLOBAL, PRIVATE. | `string` | `null` | no |
Expand Down
11 changes: 9 additions & 2 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,16 @@ module "appsync" {
}

eventbridge1 = {
type = "AMAZON_EVENTBRIDGE"
type = "AMAZON_EVENTBRIDGE"
event_bus_arn = "arn:aws:events:us-west-1:135367859850:event-bus/eventbridge1"
}

event_bus_arn = "aws:arn:events:us-west-1:135367859850:event-bus/eventbridge1"
rds1 = {
type = "RELATIONAL_DATABASE"
cluster_arn = "arn:aws:rds:us-west-1:135367859850:cluster:rds1"
secret_arn = "arn:aws:secretsmanager:us-west-1:135367859850:secret:rds-secret1"
database_name = "mydb"
schema = "myschema"
}
}

Expand Down
20 changes: 19 additions & 1 deletion iam.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
data "aws_partition" "this" {}

locals {
service_roles_with_policies = var.create_graphql_api ? { for k, v in var.datasources : k => v if contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH", "AMAZON_OPENSEARCH_SERVICE", "AMAZON_EVENTBRIDGE"], v.type) && tobool(lookup(v, "create_service_role", true)) } : {}
service_roles_with_policies = var.create_graphql_api ? { for k, v in var.datasources : k => v if contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH", "AMAZON_OPENSEARCH_SERVICE", "AMAZON_EVENTBRIDGE", "RELATIONAL_DATABASE"], v.type) && tobool(lookup(v, "create_service_role", true)) } : {}

service_roles_with_policies_lambda = { for k, v in local.service_roles_with_policies : k => merge(v,
{
Expand Down Expand Up @@ -63,12 +63,30 @@ locals {
}
) if v.type == "AMAZON_EVENTBRIDGE" }

service_roles_with_policies_relational_database = { for k, v in local.service_roles_with_policies : k => merge(v,
{
policy_statements = {
relational_database = {
effect = "Allow"
actions = lookup(v, "policy_actions", null) == null ? var.relational_database_allowed_actions : v.policy_actions
resources = [v.cluster_arn]
}
secrets_manager = {
effect = "Allow"
actions = lookup(v, "policy_actions", null) == null ? var.secrets_manager_allowed_actions : v.policy_actions
resources = [v.secret_arn]
}
}
}
) if v.type == "RELATIONAL_DATABASE" }

service_roles_with_specific_policies = merge(
local.service_roles_with_policies_lambda,
local.service_roles_with_policies_dynamodb,
local.service_roles_with_policies_elasticsearch,
local.service_roles_with_policies_opensearchservice,
local.service_roles_with_policies_eventbridge,
local.service_roles_with_policies_relational_database,
)
}

Expand Down
18 changes: 17 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ resource "aws_appsync_datasource" "this" {
name = each.key
type = each.value.type
description = lookup(each.value, "description", null)
service_role_arn = lookup(each.value, "service_role_arn", tobool(lookup(each.value, "create_service_role", contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH", "AMAZON_OPENSEARCH_SERVICE", "AMAZON_EVENTBRIDGE"], each.value.type))) ? aws_iam_role.service_role[each.key].arn : null)
service_role_arn = lookup(each.value, "service_role_arn", tobool(lookup(each.value, "create_service_role", contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH", "AMAZON_OPENSEARCH_SERVICE", "AMAZON_EVENTBRIDGE", "RELATIONAL_DATABASE"], each.value.type))) ? aws_iam_role.service_role[each.key].arn : null)

dynamic "http_config" {
for_each = each.value.type == "HTTP" ? [true] : []
Expand Down Expand Up @@ -197,6 +197,22 @@ resource "aws_appsync_datasource" "this" {
event_bus_arn = each.value.event_bus_arn
}
}

dynamic "relational_database_config" {
for_each = each.value.type == "RELATIONAL_DATABASE" ? [true] : []

content {
source_type = lookup(each.value, "source_type", "RDS_HTTP_ENDPOINT")

http_endpoint_config {
db_cluster_identifier = each.value.cluster_arn
aws_secret_store_arn = each.value.secret_arn
database_name = lookup(each.value, "database_name", null)
region = split(":", each.value.cluster_arn)[3]
schema = lookup(each.value, "schema", null)
}
}
}
}

# Resolvers
Expand Down
12 changes: 12 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,18 @@ variable "eventbridge_allowed_actions" {
default = ["events:PutEvents"]
}

variable "relational_database_allowed_actions" {
description = "List of allowed IAM actions for datasources type RELATIONAL_DATABASE"
type = list(string)
default = ["rds-data:BatchExecuteStatement", "rds-data:BeginTransaction", "rds-data:CommitTransaction", "rds-data:ExecuteStatement", "rds-data:RollbackTransaction"]
}

variable "secrets_manager_allowed_actions" {
description = "List of allowed IAM actions for secrets manager datasources type RELATIONAL_DATABASE"
type = list(string)
default = ["secretsmanager:GetSecretValue"]
}

variable "iam_permissions_boundary" {
description = "ARN for iam permissions boundary"
type = string
Expand Down
74 changes: 38 additions & 36 deletions wrappers/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,44 @@ module "wrapper" {

for_each = var.items

create_graphql_api = try(each.value.create_graphql_api, var.defaults.create_graphql_api, true)
logging_enabled = try(each.value.logging_enabled, var.defaults.logging_enabled, false)
domain_name_association_enabled = try(each.value.domain_name_association_enabled, var.defaults.domain_name_association_enabled, false)
caching_enabled = try(each.value.caching_enabled, var.defaults.caching_enabled, false)
xray_enabled = try(each.value.xray_enabled, var.defaults.xray_enabled, false)
name = try(each.value.name, var.defaults.name, "")
schema = try(each.value.schema, var.defaults.schema, "")
visibility = try(each.value.visibility, var.defaults.visibility, null)
authentication_type = try(each.value.authentication_type, var.defaults.authentication_type, "API_KEY")
create_logs_role = try(each.value.create_logs_role, var.defaults.create_logs_role, true)
logs_role_name = try(each.value.logs_role_name, var.defaults.logs_role_name, null)
log_cloudwatch_logs_role_arn = try(each.value.log_cloudwatch_logs_role_arn, var.defaults.log_cloudwatch_logs_role_arn, null)
log_field_log_level = try(each.value.log_field_log_level, var.defaults.log_field_log_level, null)
log_exclude_verbose_content = try(each.value.log_exclude_verbose_content, var.defaults.log_exclude_verbose_content, false)
lambda_authorizer_config = try(each.value.lambda_authorizer_config, var.defaults.lambda_authorizer_config, {})
openid_connect_config = try(each.value.openid_connect_config, var.defaults.openid_connect_config, {})
user_pool_config = try(each.value.user_pool_config, var.defaults.user_pool_config, {})
additional_authentication_provider = try(each.value.additional_authentication_provider, var.defaults.additional_authentication_provider, {})
graphql_api_tags = try(each.value.graphql_api_tags, var.defaults.graphql_api_tags, {})
logs_role_tags = try(each.value.logs_role_tags, var.defaults.logs_role_tags, {})
tags = try(each.value.tags, var.defaults.tags, {})
domain_name = try(each.value.domain_name, var.defaults.domain_name, "")
domain_name_description = try(each.value.domain_name_description, var.defaults.domain_name_description, null)
certificate_arn = try(each.value.certificate_arn, var.defaults.certificate_arn, "")
caching_behavior = try(each.value.caching_behavior, var.defaults.caching_behavior, "FULL_REQUEST_CACHING")
cache_type = try(each.value.cache_type, var.defaults.cache_type, "SMALL")
cache_ttl = try(each.value.cache_ttl, var.defaults.cache_ttl, 1)
cache_at_rest_encryption_enabled = try(each.value.cache_at_rest_encryption_enabled, var.defaults.cache_at_rest_encryption_enabled, false)
cache_transit_encryption_enabled = try(each.value.cache_transit_encryption_enabled, var.defaults.cache_transit_encryption_enabled, false)
api_keys = try(each.value.api_keys, var.defaults.api_keys, {})
lambda_allowed_actions = try(each.value.lambda_allowed_actions, var.defaults.lambda_allowed_actions, ["lambda:invokeFunction"])
dynamodb_allowed_actions = try(each.value.dynamodb_allowed_actions, var.defaults.dynamodb_allowed_actions, ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:UpdateItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:BatchGetItem", "dynamodb:BatchWriteItem"])
elasticsearch_allowed_actions = try(each.value.elasticsearch_allowed_actions, var.defaults.elasticsearch_allowed_actions, ["es:ESHttpDelete", "es:ESHttpHead", "es:ESHttpGet", "es:ESHttpPost", "es:ESHttpPut"])
opensearchservice_allowed_actions = try(each.value.opensearchservice_allowed_actions, var.defaults.opensearchservice_allowed_actions, ["es:ESHttpDelete", "es:ESHttpHead", "es:ESHttpGet", "es:ESHttpPost", "es:ESHttpPut"])
eventbridge_allowed_actions = try(each.value.eventbridge_allowed_actions, var.defaults.eventbridge_allowed_actions, ["events:PutEvents"])
iam_permissions_boundary = try(each.value.iam_permissions_boundary, var.defaults.iam_permissions_boundary, null)
create_graphql_api = try(each.value.create_graphql_api, var.defaults.create_graphql_api, true)
logging_enabled = try(each.value.logging_enabled, var.defaults.logging_enabled, false)
domain_name_association_enabled = try(each.value.domain_name_association_enabled, var.defaults.domain_name_association_enabled, false)
caching_enabled = try(each.value.caching_enabled, var.defaults.caching_enabled, false)
xray_enabled = try(each.value.xray_enabled, var.defaults.xray_enabled, false)
name = try(each.value.name, var.defaults.name, "")
schema = try(each.value.schema, var.defaults.schema, "")
visibility = try(each.value.visibility, var.defaults.visibility, null)
authentication_type = try(each.value.authentication_type, var.defaults.authentication_type, "API_KEY")
create_logs_role = try(each.value.create_logs_role, var.defaults.create_logs_role, true)
logs_role_name = try(each.value.logs_role_name, var.defaults.logs_role_name, null)
log_cloudwatch_logs_role_arn = try(each.value.log_cloudwatch_logs_role_arn, var.defaults.log_cloudwatch_logs_role_arn, null)
log_field_log_level = try(each.value.log_field_log_level, var.defaults.log_field_log_level, null)
log_exclude_verbose_content = try(each.value.log_exclude_verbose_content, var.defaults.log_exclude_verbose_content, false)
lambda_authorizer_config = try(each.value.lambda_authorizer_config, var.defaults.lambda_authorizer_config, {})
openid_connect_config = try(each.value.openid_connect_config, var.defaults.openid_connect_config, {})
user_pool_config = try(each.value.user_pool_config, var.defaults.user_pool_config, {})
additional_authentication_provider = try(each.value.additional_authentication_provider, var.defaults.additional_authentication_provider, {})
graphql_api_tags = try(each.value.graphql_api_tags, var.defaults.graphql_api_tags, {})
logs_role_tags = try(each.value.logs_role_tags, var.defaults.logs_role_tags, {})
tags = try(each.value.tags, var.defaults.tags, {})
domain_name = try(each.value.domain_name, var.defaults.domain_name, "")
domain_name_description = try(each.value.domain_name_description, var.defaults.domain_name_description, null)
certificate_arn = try(each.value.certificate_arn, var.defaults.certificate_arn, "")
caching_behavior = try(each.value.caching_behavior, var.defaults.caching_behavior, "FULL_REQUEST_CACHING")
cache_type = try(each.value.cache_type, var.defaults.cache_type, "SMALL")
cache_ttl = try(each.value.cache_ttl, var.defaults.cache_ttl, 1)
cache_at_rest_encryption_enabled = try(each.value.cache_at_rest_encryption_enabled, var.defaults.cache_at_rest_encryption_enabled, false)
cache_transit_encryption_enabled = try(each.value.cache_transit_encryption_enabled, var.defaults.cache_transit_encryption_enabled, false)
api_keys = try(each.value.api_keys, var.defaults.api_keys, {})
lambda_allowed_actions = try(each.value.lambda_allowed_actions, var.defaults.lambda_allowed_actions, ["lambda:invokeFunction"])
dynamodb_allowed_actions = try(each.value.dynamodb_allowed_actions, var.defaults.dynamodb_allowed_actions, ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:UpdateItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:BatchGetItem", "dynamodb:BatchWriteItem"])
elasticsearch_allowed_actions = try(each.value.elasticsearch_allowed_actions, var.defaults.elasticsearch_allowed_actions, ["es:ESHttpDelete", "es:ESHttpHead", "es:ESHttpGet", "es:ESHttpPost", "es:ESHttpPut"])
opensearchservice_allowed_actions = try(each.value.opensearchservice_allowed_actions, var.defaults.opensearchservice_allowed_actions, ["es:ESHttpDelete", "es:ESHttpHead", "es:ESHttpGet", "es:ESHttpPost", "es:ESHttpPut"])
eventbridge_allowed_actions = try(each.value.eventbridge_allowed_actions, var.defaults.eventbridge_allowed_actions, ["events:PutEvents"])
relational_database_allowed_actions = try(each.value.relational_database_allowed_actions, var.defaults.relational_database_allowed_actions, ["rds-data:BatchExecuteStatement", "rds-data:BeginTransaction", "rds-data:CommitTransaction", "rds-data:ExecuteStatement", "rds-data:RollbackTransaction"])
secrets_manager_allowed_actions = try(each.value.secrets_manager_allowed_actions, var.defaults.secrets_manager_allowed_actions, ["secretsmanager:GetSecretValue"])
iam_permissions_boundary = try(each.value.iam_permissions_boundary, var.defaults.iam_permissions_boundary, null)
direct_lambda_request_template = try(each.value.direct_lambda_request_template, var.defaults.direct_lambda_request_template, <<-EOF
{
"version" : "2017-02-28",
Expand Down

0 comments on commit 87ff09b

Please sign in to comment.