├── examples └── complete │ ├── variables.tf │ ├── versions.tf │ ├── schema.graphql │ ├── outputs.tf │ ├── README.md │ └── main.tf ├── versions.tf ├── .editorconfig ├── .github └── workflows │ ├── release.yml │ └── pre-commit.yml ├── .gitignore ├── .releaserc.json ├── .pre-commit-config.yaml ├── outputs.tf ├── CHANGELOG.md ├── iam.tf ├── variables.tf ├── main.tf ├── LICENSE └── README.md /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13.1" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 2.46" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13.1" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 2.46" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/complete/schema.graphql: -------------------------------------------------------------------------------- 1 | type Mutation { 2 | putPost(id: ID!, title: String!): Post 3 | } 4 | 5 | type Post { 6 | id: ID! 7 | title: String! 8 | } 9 | 10 | type Query { 11 | singlePost(id: ID!): Post 12 | none(dummyString: String!): String! 13 | } 14 | 15 | schema { 16 | query: Query 17 | mutation: Mutation 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.py] 18 | indent_size = 4 19 | 20 | [*.{tf,tfvars}] 21 | indent_size = 2 22 | indent_style = space 23 | 24 | [*.md] 25 | max_line_length = 0 26 | trim_trailing_whitespace = false 27 | 28 | [Makefile] 29 | tab_width = 2 30 | indent_style = tab 31 | 32 | [COMMIT_EDITMSG] 33 | max_line_length = 0 34 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths: 10 | - '**/*.py' 11 | - '**/*.tf' 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | with: 21 | persist-credentials: false 22 | fetch-depth: 0 23 | 24 | - name: Release 25 | uses: cycjimmy/semantic-release-action@v2 26 | with: 27 | semantic_version: 18.0.0 28 | extra_plugins: | 29 | @semantic-release/changelog@6.0.0 30 | @semantic-release/git@10.0.0 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # Terraform lockfile 5 | .terraform.lock.hcl 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | *.tfplan 11 | 12 | # Crash log files 13 | crash.log 14 | 15 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 16 | # password, private keys, and other secrets. These should not be part of version 17 | # control as they are data points which are potentially sensitive and subject 18 | # to change depending on the environment. 19 | *.tfvars 20 | 21 | # Ignore override files as they are usually used to override resources locally and so 22 | # are not checked in 23 | override.tf 24 | override.tf.json 25 | *_override.tf 26 | *_override.tf.json 27 | 28 | # Ignore CLI configuration files 29 | .terraformrc 30 | terraform.rc 31 | 32 | # Lambda directories 33 | builds/ 34 | __pycache__/ 35 | *.zip 36 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "ci": false, 7 | "plugins": [ 8 | "@semantic-release/commit-analyzer", 9 | "@semantic-release/release-notes-generator", 10 | [ 11 | "@semantic-release/github", 12 | { 13 | "successComment": 14 | "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", 15 | "labels": false, 16 | "releasedLabels": false 17 | } 18 | ], 19 | [ 20 | "@semantic-release/changelog", 21 | { 22 | "changelogFile": "CHANGELOG.md", 23 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." 24 | } 25 | ], 26 | [ 27 | "@semantic-release/git", 28 | { 29 | "assets": [ 30 | "CHANGELOG.md" 31 | ], 32 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 33 | } 34 | ] 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.58.0 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_validate 7 | - id: terraform_docs 8 | args: 9 | - '--args=--lockfile=false' 10 | - id: terraform_tflint 11 | args: 12 | - '--args=--only=terraform_deprecated_interpolation' 13 | - '--args=--only=terraform_deprecated_index' 14 | - '--args=--only=terraform_unused_declarations' 15 | - '--args=--only=terraform_comment_syntax' 16 | - '--args=--only=terraform_documented_outputs' 17 | - '--args=--only=terraform_documented_variables' 18 | - '--args=--only=terraform_typed_variables' 19 | - '--args=--only=terraform_module_pinned_source' 20 | - '--args=--only=terraform_naming_convention' 21 | - '--args=--only=terraform_required_version' 22 | - '--args=--only=terraform_required_providers' 23 | - '--args=--only=terraform_standard_module_structure' 24 | - '--args=--only=terraform_workspace_remote' 25 | - repo: https://github.com/pre-commit/pre-commit-hooks 26 | rev: v4.0.1 27 | hooks: 28 | - id: check-merge-conflict 29 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | # GraphQL API 2 | output "appsync_graphql_api_id" { 3 | description = "ID of GraphQL API" 4 | value = module.appsync.appsync_graphql_api_id 5 | } 6 | 7 | output "appsync_graphql_api_arn" { 8 | description = "ARN of GraphQL API" 9 | value = module.appsync.appsync_graphql_api_arn 10 | } 11 | 12 | output "appsync_graphql_api_uris" { 13 | description = "Map of URIs associated with the API" 14 | value = module.appsync.appsync_graphql_api_uris 15 | } 16 | 17 | # API Key 18 | output "appsync_api_key_id" { 19 | description = "Map of API Key ID (Formatted as ApiId:Key)" 20 | value = module.appsync.appsync_api_key_id 21 | } 22 | 23 | output "appsync_api_key_key" { 24 | description = "Map of API Keys" 25 | value = module.appsync.appsync_api_key_key 26 | } 27 | 28 | # Datasources 29 | output "appsync_datasource_arn" { 30 | description = "Map of ARNs of datasources" 31 | value = module.appsync.appsync_datasource_arn 32 | } 33 | 34 | # Resolvers 35 | output "appsync_resolver_arn" { 36 | description = "Map of ARNs of resolvers" 37 | value = module.appsync.appsync_resolver_arn 38 | } 39 | 40 | # Extra 41 | output "appsync_graphql_api_fqdns" { 42 | description = "Map of FQDNs associated with the API (no protocol and path)" 43 | value = module.appsync.appsync_graphql_api_fqdns 44 | } 45 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # GraphQL API 2 | output "appsync_graphql_api_id" { 3 | description = "ID of GraphQL API" 4 | value = element(concat(aws_appsync_graphql_api.this.*.id, [""]), 0) 5 | } 6 | 7 | output "appsync_graphql_api_arn" { 8 | description = "ARN of GraphQL API" 9 | value = element(concat(aws_appsync_graphql_api.this.*.arn, [""]), 0) 10 | } 11 | 12 | output "appsync_graphql_api_uris" { 13 | description = "Map of URIs associated with the API" 14 | value = element(concat(aws_appsync_graphql_api.this.*.uris, [""]), 0) 15 | } 16 | 17 | # API Key 18 | output "appsync_api_key_id" { 19 | description = "Map of API Key ID (Formatted as ApiId:Key)" 20 | value = { for k, v in aws_appsync_api_key.this : k => v.id } 21 | } 22 | 23 | output "appsync_api_key_key" { 24 | description = "Map of API Keys" 25 | value = { for k, v in aws_appsync_api_key.this : k => v.key } 26 | } 27 | 28 | # Datasources 29 | output "appsync_datasource_arn" { 30 | description = "Map of ARNs of datasources" 31 | value = { for k, v in aws_appsync_datasource.this : k => v.arn } 32 | } 33 | 34 | # Resolvers 35 | output "appsync_resolver_arn" { 36 | description = "Map of ARNs of resolvers" 37 | value = { for k, v in aws_appsync_resolver.this : k => v.arn } 38 | } 39 | 40 | # Functions 41 | output "appsync_function_arn" { 42 | description = "Map of ARNs of functions" 43 | value = { for k, v in aws_appsync_function.this : k => v.arn } 44 | } 45 | 46 | output "appsync_function_id" { 47 | description = "Map of IDs of functions" 48 | value = { for k, v in aws_appsync_function.this : k => v.id } 49 | } 50 | 51 | output "appsync_function_function_id" { 52 | description = "Map of function IDs of functions" 53 | value = { for k, v in aws_appsync_function.this : k => v.function_id } 54 | } 55 | 56 | # Extra 57 | output "appsync_graphql_api_fqdns" { 58 | description = "Map of FQDNs associated with the API (no protocol and path)" 59 | value = length(aws_appsync_graphql_api.this) != 0 ? { for k, v in element(concat(aws_appsync_graphql_api.this.*.uris, [""]), 0) : k => regex("://([^/?#]*)?", v)[0] } : {} 60 | } 61 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - master 8 | 9 | env: 10 | TERRAFORM_DOCS_VERSION: v0.16.0 11 | 12 | jobs: 13 | collectInputs: 14 | name: Collect workflow inputs 15 | runs-on: ubuntu-latest 16 | outputs: 17 | directories: ${{ steps.dirs.outputs.directories }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Get root directories 23 | id: dirs 24 | uses: clowdhaus/terraform-composite-actions/directories@v1.3.0 25 | 26 | preCommitMinVersions: 27 | name: Min TF pre-commit 28 | needs: collectInputs 29 | runs-on: ubuntu-latest 30 | strategy: 31 | matrix: 32 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v2 36 | 37 | - name: Terraform min/max versions 38 | id: minMax 39 | uses: clowdhaus/terraform-min-max@v1.0.3 40 | with: 41 | directory: ${{ matrix.directory }} 42 | 43 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 44 | # Run only validate pre-commit check on min version supported 45 | if: ${{ matrix.directory != '.' }} 46 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.3.0 47 | with: 48 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 49 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' 50 | 51 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 52 | # Run only validate pre-commit check on min version supported 53 | if: ${{ matrix.directory == '.' }} 54 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.3.0 55 | with: 56 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 57 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' 58 | 59 | preCommitMaxVersion: 60 | name: Max TF pre-commit 61 | runs-on: ubuntu-latest 62 | needs: collectInputs 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v2 66 | with: 67 | ref: ${{ github.event.pull_request.head.ref }} 68 | repository: ${{github.event.pull_request.head.repo.full_name}} 69 | 70 | - name: Terraform min/max versions 71 | id: minMax 72 | uses: clowdhaus/terraform-min-max@v1.0.3 73 | 74 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} 75 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.3.0 76 | with: 77 | terraform-version: ${{ steps.minMax.outputs.maxVersion }} 78 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} 79 | -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # Complete AWS AppSync example 2 | 3 | Configuration in this directory creates AWS AppSync with various types of datasources and resolvers. 4 | 5 | 6 | ## Usage 7 | 8 | To run this example you need to execute: 9 | 10 | ```bash 11 | $ terraform init 12 | $ terraform plan 13 | $ terraform apply 14 | ``` 15 | 16 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 17 | 18 | 19 | ## Requirements 20 | 21 | | Name | Version | 22 | |------|---------| 23 | | [terraform](#requirement\_terraform) | >= 0.13.1 | 24 | | [aws](#requirement\_aws) | >= 2.46 | 25 | | [random](#requirement\_random) | >= 2.0 | 26 | 27 | ## Providers 28 | 29 | | Name | Version | 30 | |------|---------| 31 | | [aws](#provider\_aws) | >= 2.46 | 32 | | [random](#provider\_random) | >= 2.0 | 33 | 34 | ## Modules 35 | 36 | | Name | Source | Version | 37 | |------|--------|---------| 38 | | [appsync](#module\_appsync) | ../../ | n/a | 39 | | [disabled](#module\_disabled) | ../../ | n/a | 40 | 41 | ## Resources 42 | 43 | | Name | Type | 44 | |------|------| 45 | | [aws_cognito_user_pool.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool) | resource | 46 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 47 | 48 | ## Inputs 49 | 50 | No inputs. 51 | 52 | ## Outputs 53 | 54 | | Name | Description | 55 | |------|-------------| 56 | | [appsync\_api\_key\_id](#output\_appsync\_api\_key\_id) | Map of API Key ID (Formatted as ApiId:Key) | 57 | | [appsync\_api\_key\_key](#output\_appsync\_api\_key\_key) | Map of API Keys | 58 | | [appsync\_datasource\_arn](#output\_appsync\_datasource\_arn) | Map of ARNs of datasources | 59 | | [appsync\_graphql\_api\_arn](#output\_appsync\_graphql\_api\_arn) | ARN of GraphQL API | 60 | | [appsync\_graphql\_api\_fqdns](#output\_appsync\_graphql\_api\_fqdns) | Map of FQDNs associated with the API (no protocol and path) | 61 | | [appsync\_graphql\_api\_id](#output\_appsync\_graphql\_api\_id) | ID of GraphQL API | 62 | | [appsync\_graphql\_api\_uris](#output\_appsync\_graphql\_api\_uris) | Map of URIs associated with the API | 63 | | [appsync\_resolver\_arn](#output\_appsync\_resolver\_arn) | Map of ARNs of resolvers | 64 | 65 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [1.1.1](https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v1.1.0...v1.1.1) (2021-11-22) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * update CI/CD process to enable auto-release workflow ([#22](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/22)) ([6cedbdf](https://github.com/terraform-aws-modules/terraform-aws-appsync/commit/6cedbdf0c1fe8051a90b7cbb4a0963c75dc26aba)) 11 | 12 | 13 | ## [v1.1.0] - 2021-09-08 14 | 15 | - feat: Add iam_permission_boundary to IAM role ([#18](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/18)) 16 | - chore: update CI/CD to use stable `terraform-docs` release artifact and discoverable Apache2.0 license ([#15](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/15)) 17 | - chore: Updated versions&comments in examples 18 | 19 | 20 | 21 | ## [v1.0.0] - 2021-04-26 22 | 23 | - feat: Shorten outputs (removing this_) ([#14](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/14)) 24 | 25 | 26 | 27 | ## [v0.9.0] - 2021-04-22 28 | 29 | - feat: add support for functions ([#13](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/13)) 30 | - chore: update documentation and pin `terraform_docs` version to avoid future changes ([#12](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/12)) 31 | - chore: align ci-cd static checks to use individual minimum Terraform versions ([#11](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/11)) 32 | - chore: add ci-cd workflow for pre-commit checks ([#10](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/10)) 33 | 34 | 35 | 36 | ## [v0.8.0] - 2020-12-06 37 | 38 | - fix: dynamodb service role ([#7](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/7)) 39 | 40 | 41 | 42 | ## [v0.7.0] - 2020-11-16 43 | 44 | - fix: Fixed terraform versions ([#6](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/6)) 45 | 46 | 47 | 48 | ## [v0.6.0] - 2020-11-16 49 | 50 | - fix: terraform output after the resources are destroyed without any error ([#5](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/5)) 51 | - Added disabled example 52 | - Update outputs.tf 53 | 54 | 55 | 56 | ## [v0.5.0] - 2020-10-26 57 | 58 | - fix: Fixed ARN for DynamoDB table in IAM role created by AppSync 59 | 60 | 61 | 62 | ## [v0.4.0] - 2020-10-04 63 | 64 | - feat: Added support for all authorization providers ([#2](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/2)) 65 | 66 | 67 | 68 | ## [v0.3.0] - 2020-09-15 69 | 70 | - Added GraphQL FQDN to outputs 71 | - docs: Document migration path for Terraform resources (datasource vs resolver) 72 | 73 | 74 | 75 | ## [v0.2.0] - 2020-08-25 76 | 77 | - fix: Updated VTL templates to reflect direct Lambda resolvers integration ([#1](https://github.com/terraform-aws-modules/terraform-aws-appsync/issues/1)) 78 | 79 | 80 | 81 | ## v0.1.0 - 2020-08-24 82 | 83 | - Add all the code for AppSync module 84 | 85 | 86 | [Unreleased]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v1.1.0...HEAD 87 | [v1.1.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v1.0.0...v1.1.0 88 | [v1.0.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.9.0...v1.0.0 89 | [v0.9.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.8.0...v0.9.0 90 | [v0.8.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.7.0...v0.8.0 91 | [v0.7.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.6.0...v0.7.0 92 | [v0.6.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.5.0...v0.6.0 93 | [v0.5.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.4.0...v0.5.0 94 | [v0.4.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.3.0...v0.4.0 95 | [v0.3.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.2.0...v0.3.0 96 | [v0.2.0]: https://github.com/terraform-aws-modules/terraform-aws-appsync/compare/v0.1.0...v0.2.0 97 | -------------------------------------------------------------------------------- /iam.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | service_roles_with_policies = var.create_graphql_api ? { for k, v in var.datasources : k => v if contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH"], v.type) && tobool(lookup(v, "create_service_role", true)) } : {} 3 | 4 | service_roles_with_policies_lambda = { for k, v in local.service_roles_with_policies : k => merge(v, 5 | { 6 | policy_statements = { 7 | lambda = { 8 | effect = "Allow" 9 | actions = lookup(v, "policy_actions", null) == null ? var.lambda_allowed_actions : v.policy_actions 10 | resources = [for _, f in ["%v", "%v:*"] : format(f, v.function_arn)] 11 | } 12 | } 13 | } 14 | ) if v.type == "AWS_LAMBDA" } 15 | 16 | service_roles_with_policies_dynamodb = { for k, v in local.service_roles_with_policies : k => merge(v, 17 | { 18 | policy_statements = { 19 | dynamodb = { 20 | effect = "Allow" 21 | actions = lookup(v, "policy_actions", null) == null ? var.dynamodb_allowed_actions : v.policy_actions 22 | resources = [for _, f in ["arn:aws:dynamodb:%v:%v:table/%v", "arn:aws:dynamodb:%v:%v:table/%v/*"] : format(f, v.region, lookup(v, "aws_account_id", data.aws_caller_identity.this.account_id), v.table_name)] 23 | } 24 | } 25 | } 26 | ) if v.type == "AMAZON_DYNAMODB" } 27 | 28 | service_roles_with_policies_elasticsearch = { for k, v in local.service_roles_with_policies : k => merge(v, 29 | { 30 | policy_statements = { 31 | elasticsearch = { 32 | effect = "Allow" 33 | actions = lookup(v, "policy_actions", null) == null ? var.elasticsearch_allowed_actions : v.policy_actions 34 | resources = [format("arn:aws:es:%v::domain/%v/*", v.region, v.endpoint)] 35 | } 36 | } 37 | } 38 | ) if v.type == "AMAZON_ELASTICSEARCH" } 39 | 40 | service_roles_with_specific_policies = merge( 41 | local.service_roles_with_policies_lambda, 42 | local.service_roles_with_policies_dynamodb, 43 | local.service_roles_with_policies_elasticsearch, 44 | ) 45 | } 46 | 47 | data "aws_caller_identity" "this" {} 48 | 49 | data "aws_iam_policy_document" "assume_role" { 50 | statement { 51 | effect = "Allow" 52 | actions = ["sts:AssumeRole"] 53 | 54 | principals { 55 | type = "Service" 56 | identifiers = ["appsync.amazonaws.com"] 57 | } 58 | } 59 | } 60 | 61 | # Logs 62 | resource "aws_iam_role" "logs" { 63 | count = var.logging_enabled && var.create_logs_role ? 1 : 0 64 | 65 | name = coalesce(var.logs_role_name, "${var.name}-logs") 66 | assume_role_policy = data.aws_iam_policy_document.assume_role.json 67 | permissions_boundary = var.iam_permissions_boundary 68 | 69 | tags = merge(var.tags, var.logs_role_tags) 70 | } 71 | 72 | resource "aws_iam_role_policy_attachment" "logs" { 73 | count = var.logging_enabled && var.create_logs_role ? 1 : 0 74 | 75 | policy_arn = "arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs" 76 | role = aws_iam_role.logs[0].name 77 | } 78 | 79 | # Service role for datasource 80 | resource "aws_iam_role" "service_role" { 81 | for_each = local.service_roles_with_specific_policies 82 | 83 | name = lookup(each.value, "service_role_name", "${each.key}-role") 84 | permissions_boundary = var.iam_permissions_boundary 85 | assume_role_policy = data.aws_iam_policy_document.assume_role.json 86 | } 87 | 88 | resource "aws_iam_role_policy" "this" { 89 | for_each = local.service_roles_with_specific_policies 90 | 91 | name = "service-policy" 92 | role = aws_iam_role.service_role[each.key].id 93 | policy = data.aws_iam_policy_document.service_policy[each.key].json 94 | } 95 | 96 | data "aws_iam_policy_document" "service_policy" { 97 | for_each = local.service_roles_with_specific_policies 98 | 99 | dynamic "statement" { 100 | for_each = each.value.policy_statements 101 | 102 | content { 103 | sid = lookup(statement.value, "sid", replace(statement.key, "/[^0-9A-Za-z]*/", "")) 104 | effect = lookup(statement.value, "effect", null) 105 | actions = lookup(statement.value, "actions", null) 106 | not_actions = lookup(statement.value, "not_actions", null) 107 | resources = lookup(statement.value, "resources", null) 108 | not_resources = lookup(statement.value, "not_resources", null) 109 | 110 | dynamic "principals" { 111 | for_each = lookup(statement.value, "principals", []) 112 | content { 113 | type = principals.value.type 114 | identifiers = principals.value.identifiers 115 | } 116 | } 117 | 118 | dynamic "not_principals" { 119 | for_each = lookup(statement.value, "not_principals", []) 120 | content { 121 | type = not_principals.value.type 122 | identifiers = not_principals.value.identifiers 123 | } 124 | } 125 | 126 | dynamic "condition" { 127 | for_each = lookup(statement.value, "condition", []) 128 | content { 129 | test = condition.value.test 130 | variable = condition.value.variable 131 | values = condition.value.values 132 | } 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | 4 | # Make it faster by skipping something 5 | skip_get_ec2_platforms = true 6 | skip_metadata_api_check = true 7 | skip_region_validation = true 8 | skip_credentials_validation = true 9 | 10 | # skip_requesting_account_id should be disabled to generate valid ARN in apigatewayv2_api_execution_arn 11 | skip_requesting_account_id = false 12 | } 13 | 14 | module "appsync" { 15 | source = "../../" 16 | 17 | name = random_pet.this.id 18 | 19 | schema = file("schema.graphql") 20 | 21 | api_keys = { 22 | future = "2021-08-20T15:00:00Z" 23 | default = null 24 | } 25 | 26 | authentication_type = "OPENID_CONNECT" 27 | 28 | openid_connect_config = { 29 | issuer = "https://www.issuer1.com/" 30 | client_id = "client_id1" 31 | auth_ttl = 100 32 | iat_ttl = 200 33 | } 34 | 35 | additional_authentication_provider = { 36 | iam = { 37 | authentication_type = "AWS_IAM" 38 | } 39 | openid_connect_2 = { 40 | authentication_type = "OPENID_CONNECT" 41 | 42 | openid_connect_config = { 43 | issuer = "https://www.issuer2.com/" 44 | client_id = "client_id2" 45 | } 46 | } 47 | 48 | my_user_pool = { 49 | authentication_type = "AMAZON_COGNITO_USER_POOLS" 50 | 51 | user_pool_config = { 52 | user_pool_id = aws_cognito_user_pool.this.id 53 | } 54 | } 55 | } 56 | 57 | functions = { 58 | None = { 59 | kind = "PIPELINE" 60 | type = "Query" 61 | data_source = "None" 62 | request_mapping_template = < merge(v, { 3 | type = split(".", k)[0] 4 | field = join(".", slice(split(".", k), 1, length(split(".", k)))) 5 | }) } : tomap({}) 6 | } 7 | 8 | # GraphQL API 9 | resource "aws_appsync_graphql_api" "this" { 10 | count = var.create_graphql_api ? 1 : 0 11 | 12 | name = var.name 13 | authentication_type = var.authentication_type 14 | schema = var.schema 15 | xray_enabled = var.xray_enabled 16 | 17 | dynamic "log_config" { 18 | for_each = var.logging_enabled ? [true] : [] 19 | 20 | content { 21 | cloudwatch_logs_role_arn = var.create_logs_role ? aws_iam_role.logs[0].arn : var.log_cloudwatch_logs_role_arn 22 | field_log_level = var.log_field_log_level 23 | exclude_verbose_content = var.log_exclude_verbose_content 24 | } 25 | } 26 | 27 | dynamic "openid_connect_config" { 28 | for_each = length(keys(var.openid_connect_config)) == 0 ? [] : [true] 29 | 30 | content { 31 | issuer = var.openid_connect_config["issuer"] 32 | client_id = lookup(var.openid_connect_config, "client_id", null) 33 | auth_ttl = lookup(var.openid_connect_config, "auth_ttl", null) 34 | iat_ttl = lookup(var.openid_connect_config, "iat_ttl", null) 35 | } 36 | } 37 | 38 | dynamic "user_pool_config" { 39 | for_each = length(keys(var.user_pool_config)) == 0 ? [] : [true] 40 | 41 | content { 42 | default_action = var.user_pool_config["default_action"] 43 | user_pool_id = var.user_pool_config["user_pool_id"] 44 | app_id_client_regex = lookup(var.openid_connect_config, "app_id_client_regex", null) 45 | aws_region = lookup(var.openid_connect_config, "aws_region", null) 46 | } 47 | } 48 | 49 | dynamic "additional_authentication_provider" { 50 | for_each = var.additional_authentication_provider 51 | 52 | content { 53 | authentication_type = additional_authentication_provider.value.authentication_type 54 | 55 | dynamic "openid_connect_config" { 56 | for_each = length(keys(lookup(additional_authentication_provider.value, "openid_connect_config", {}))) == 0 ? [] : [additional_authentication_provider.value.openid_connect_config] 57 | 58 | content { 59 | issuer = openid_connect_config.value.issuer 60 | client_id = lookup(openid_connect_config.value, "client_id", null) 61 | auth_ttl = lookup(openid_connect_config.value, "auth_ttl", null) 62 | iat_ttl = lookup(openid_connect_config.value, "iat_ttl", null) 63 | } 64 | } 65 | 66 | dynamic "user_pool_config" { 67 | for_each = length(keys(lookup(additional_authentication_provider.value, "user_pool_config", {}))) == 0 ? [] : [additional_authentication_provider.value.user_pool_config] 68 | 69 | content { 70 | user_pool_id = user_pool_config.value.user_pool_id 71 | app_id_client_regex = lookup(user_pool_config.value, "app_id_client_regex", null) 72 | aws_region = lookup(user_pool_config.value, "aws_region", null) 73 | } 74 | } 75 | } 76 | } 77 | 78 | tags = merge({ Name = var.name }, var.graphql_api_tags) 79 | } 80 | 81 | # API Key 82 | resource "aws_appsync_api_key" "this" { 83 | for_each = var.create_graphql_api && var.authentication_type == "API_KEY" ? var.api_keys : {} 84 | 85 | api_id = aws_appsync_graphql_api.this[0].id 86 | description = each.key 87 | expires = each.value 88 | } 89 | 90 | # Datasource 91 | resource "aws_appsync_datasource" "this" { 92 | for_each = var.create_graphql_api ? var.datasources : {} 93 | 94 | api_id = aws_appsync_graphql_api.this[0].id 95 | name = each.key 96 | type = each.value.type 97 | description = lookup(each.value, "description", null) 98 | service_role_arn = lookup(each.value, "service_role_arn", tobool(lookup(each.value, "create_service_role", contains(["AWS_LAMBDA", "AMAZON_DYNAMODB", "AMAZON_ELASTICSEARCH"], each.value.type))) ? aws_iam_role.service_role[each.key].arn : null) 99 | 100 | dynamic "http_config" { 101 | for_each = each.value.type == "HTTP" ? [true] : [] 102 | 103 | content { 104 | endpoint = each.value.endpoint 105 | } 106 | } 107 | 108 | dynamic "lambda_config" { 109 | for_each = each.value.type == "AWS_LAMBDA" ? [true] : [] 110 | 111 | content { 112 | function_arn = each.value.function_arn 113 | } 114 | } 115 | 116 | dynamic "dynamodb_config" { 117 | for_each = each.value.type == "AMAZON_DYNAMODB" ? [true] : [] 118 | 119 | content { 120 | table_name = each.value.table_name 121 | region = lookup(each.value, "region", null) 122 | use_caller_credentials = lookup(each.value, "use_caller_credentials", null) 123 | } 124 | } 125 | 126 | dynamic "elasticsearch_config" { 127 | for_each = each.value.type == "AMAZON_ELASTICSEARCH" ? [true] : [] 128 | 129 | content { 130 | endpoint = each.value.endpoint 131 | region = lookup(each.value, "region", null) 132 | } 133 | } 134 | } 135 | 136 | # Resolvers 137 | resource "aws_appsync_resolver" "this" { 138 | for_each = local.resolvers 139 | 140 | api_id = aws_appsync_graphql_api.this[0].id 141 | type = each.value.type 142 | field = each.value.field 143 | kind = lookup(each.value, "kind", null) 144 | 145 | request_template = lookup(each.value, "request_template", tobool(lookup(each.value, "direct_lambda", false)) ? var.direct_lambda_request_template : "{}") 146 | response_template = lookup(each.value, "response_template", tobool(lookup(each.value, "direct_lambda", false)) ? var.direct_lambda_response_template : "{}") 147 | 148 | data_source = lookup(each.value, "data_source", null) != null ? aws_appsync_datasource.this[each.value.data_source].name : lookup(each.value, "data_source_arn", null) 149 | 150 | dynamic "pipeline_config" { 151 | for_each = lookup(each.value, "functions", null) != null ? [true] : [] 152 | 153 | content { 154 | functions = [for k in each.value.functions : 155 | contains(keys(aws_appsync_function.this), k) ? aws_appsync_function.this[k].function_id : k] 156 | } 157 | } 158 | 159 | dynamic "caching_config" { 160 | for_each = lookup(each.value, "caching_keys", null) != null ? [true] : [] 161 | 162 | content { 163 | caching_keys = each.value.caching_keys 164 | ttl = lookup(each.value, "caching_ttl", var.resolver_caching_ttl) 165 | } 166 | } 167 | } 168 | 169 | # Functions 170 | resource "aws_appsync_function" "this" { 171 | for_each = var.create_graphql_api ? var.functions : {} 172 | 173 | api_id = aws_appsync_graphql_api.this[0].id 174 | data_source = lookup(each.value, "data_source", null) 175 | name = each.key 176 | description = lookup(each.value, "description", null) 177 | function_version = lookup(each.value, "function_version", "2018-05-29") 178 | 179 | request_mapping_template = lookup(each.value, "request_mapping_template", null) 180 | response_mapping_template = lookup(each.value, "response_mapping_template", null) 181 | } 182 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS AppSync Terraform module 2 | 3 | Terraform module which creates AWS AppSync resources and connects them together. 4 | 5 | This Terraform module is part of [serverless.tf framework](https://serverless.tf), which aims to simplify all operations when working with the serverless in Terraform. 6 | 7 | ## Usage 8 | 9 | ### Complete AppSync with datasources and resolvers 10 | 11 | ```hcl 12 | module "appsync" { 13 | source = "terraform-aws-modules/appsync/aws" 14 | 15 | name = "dev-appsync" 16 | 17 | schema = file("schema.graphql") 18 | 19 | api_keys = { 20 | default = null # such key will expire in 7 days 21 | } 22 | 23 | additional_authentication_provider = { 24 | iam = { 25 | authentication_type = "AWS_IAM" 26 | } 27 | 28 | openid_connect_1 = { 29 | authentication_type = "OPENID_CONNECT" 30 | 31 | openid_connect_config = { 32 | issuer = "https://www.issuer1.com/" 33 | client_id = "client_id1" 34 | } 35 | } 36 | } 37 | 38 | datasources = { 39 | registry_terraform_io = { 40 | type = "HTTP" 41 | endpoint = "https://registry.terraform.io" 42 | } 43 | 44 | lambda_create_zip = { 45 | type = "AWS_LAMBDA" 46 | function_arn = "arn:aws:lambda:eu-west-1:135367859850:function:index_1" 47 | } 48 | 49 | dynamodb1 = { 50 | type = "AMAZON_DYNAMODB" 51 | table_name = "my-table" 52 | region = "eu-west-1" 53 | } 54 | 55 | elasticsearch1 = { 56 | type = "AMAZON_ELASTICSEARCH" 57 | endpoint = "https://search-my-domain.eu-west-1.es.amazonaws.com" 58 | region = "eu-west-1" 59 | } 60 | } 61 | 62 | resolvers = { 63 | "Query.getZip" = { 64 | data_source = "lambda_create_zip" 65 | direct_lambda = true 66 | } 67 | 68 | "Query.getModuleFromRegistry" = { 69 | data_source = "registry_terraform_io" 70 | request_template = file("vtl-templates/request.Query.getModuleFromRegistry.vtl") 71 | response_template = file("vtl-templates/response.Query.getModuleFromRegistry.vtl") 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | ## Conditional creation 78 | 79 | Sometimes you need to have a way to create resources conditionally but Terraform 0.12 does not allow usage of `count` inside `module` block, so the solution is to specify `create_graphql_api` argument. 80 | 81 | ```hcl 82 | module "appsync" { 83 | source = "terraform-aws-modules/appsync/aws" 84 | 85 | create_graphql_api = false # to disable all resources 86 | 87 | # ... omitted 88 | } 89 | ``` 90 | 91 | ## Relationship between Data-Source and Resolver resources 92 | 93 | `datasources` define keys which can be referenced in `resolvers`. For initial configuration and parameters updates Terraform is able to understand the order of resources correctly. 94 | 95 | In order to change name of keys in both places (eg from `lambda-old` to `lambda-new`), you will need to change key in both variables, and then run Terraform with partial configuration (using `-target`) to handle the migration in the `aws_appsync_resolver` resource (eg, `Post.id`): 96 | 97 | ```shell 98 | # Create new resources and update resolver 99 | $ terraform apply -target="module.appsync.aws_appsync_resolver.this[\"Post.id\"]" -target="module.appsync.aws_appsync_datasource.this[\"lambda-new\"]" -target="module.appsync.aws_iam_role.service_role[\"lambda-new\"]" -target="module.appsync.aws_iam_role_policy.this[\"lambda-new\"]" 100 | 101 | # Delete orphan resources ("lambda-old") 102 | $ terraform apply 103 | ``` 104 | 105 | ## Examples 106 | 107 | - [Complete](https://github.com/terraform-aws-modules/terraform-aws-appsync/tree/master/examples/complete) - Create AppSync with datasources, resolvers, and authorization providers in various combinations. 108 | 109 | 110 | ## Requirements 111 | 112 | | Name | Version | 113 | |------|---------| 114 | | [terraform](#requirement\_terraform) | >= 0.13.1 | 115 | | [aws](#requirement\_aws) | >= 2.46 | 116 | | [random](#requirement\_random) | >= 2.0 | 117 | 118 | ## Providers 119 | 120 | | Name | Version | 121 | |------|---------| 122 | | [aws](#provider\_aws) | >= 2.46 | 123 | 124 | ## Modules 125 | 126 | No modules. 127 | 128 | ## Resources 129 | 130 | | Name | Type | 131 | |------|------| 132 | | [aws_appsync_api_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_api_key) | resource | 133 | | [aws_appsync_datasource.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_datasource) | resource | 134 | | [aws_appsync_function.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_function) | resource | 135 | | [aws_appsync_graphql_api.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_graphql_api) | resource | 136 | | [aws_appsync_resolver.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_resolver) | resource | 137 | | [aws_iam_role.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 138 | | [aws_iam_role.service_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 139 | | [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | 140 | | [aws_iam_role_policy_attachment.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 141 | | [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 142 | | [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 143 | | [aws_iam_policy_document.service_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 144 | 145 | ## Inputs 146 | 147 | | Name | Description | Type | Default | Required | 148 | |------|-------------|------|---------|:--------:| 149 | | [additional\_authentication\_provider](#input\_additional\_authentication\_provider) | One or more additional authentication providers for the GraphqlApi. | `any` | `{}` | no | 150 | | [api\_keys](#input\_api\_keys) | Map of API keys to create | `map(string)` | `{}` | no | 151 | | [authentication\_type](#input\_authentication\_type) | The authentication type to use by GraphQL API | `string` | `"API_KEY"` | no | 152 | | [create\_graphql\_api](#input\_create\_graphql\_api) | Whether to create GraphQL API | `bool` | `true` | no | 153 | | [create\_logs\_role](#input\_create\_logs\_role) | Whether to create service role for Cloudwatch logs | `bool` | `true` | no | 154 | | [datasources](#input\_datasources) | Map of datasources to create | `any` | `{}` | no | 155 | | [direct\_lambda\_request\_template](#input\_direct\_lambda\_request\_template) | VTL request template for the direct lambda integrations | `string` | `"{\n \"version\" : \"2017-02-28\",\n \"operation\": \"Invoke\",\n \"payload\": {\n \"arguments\": $util.toJson($ctx.arguments),\n \"identity\": $util.toJson($ctx.identity),\n \"source\": $util.toJson($ctx.source),\n \"request\": $util.toJson($ctx.request),\n \"prev\": $util.toJson($ctx.prev),\n \"info\": {\n \"selectionSetList\": $util.toJson($ctx.info.selectionSetList),\n \"selectionSetGraphQL\": $util.toJson($ctx.info.selectionSetGraphQL),\n \"parentTypeName\": $util.toJson($ctx.info.parentTypeName),\n \"fieldName\": $util.toJson($ctx.info.fieldName),\n \"variables\": $util.toJson($ctx.info.variables)\n },\n \"stash\": $util.toJson($ctx.stash)\n }\n}\n"` | no | 156 | | [direct\_lambda\_response\_template](#input\_direct\_lambda\_response\_template) | VTL response template for the direct lambda integrations | `string` | `"$util.toJson($ctx.result)\n"` | no | 157 | | [dynamodb\_allowed\_actions](#input\_dynamodb\_allowed\_actions) | List of allowed IAM actions for datasources type AMAZON\_DYNAMODB | `list(string)` |
[
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:UpdateItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem"
]
| no | 158 | | [elasticsearch\_allowed\_actions](#input\_elasticsearch\_allowed\_actions) | List of allowed IAM actions for datasources type AMAZON\_ELASTICSEARCH | `list(string)` |
[
"es:ESHttpDelete",
"es:ESHttpHead",
"es:ESHttpGet",
"es:ESHttpPost",
"es:ESHttpPut"
]
| no | 159 | | [functions](#input\_functions) | Map of functions to create | `any` | `{}` | no | 160 | | [graphql\_api\_tags](#input\_graphql\_api\_tags) | Map of tags to add to GraphQL API | `map(string)` | `{}` | no | 161 | | [iam\_permissions\_boundary](#input\_iam\_permissions\_boundary) | ARN for iam permissions boundary | `string` | `null` | no | 162 | | [lambda\_allowed\_actions](#input\_lambda\_allowed\_actions) | List of allowed IAM actions for datasources type AWS\_LAMBDA | `list(string)` |
[
"lambda:invokeFunction"
]
| no | 163 | | [log\_cloudwatch\_logs\_role\_arn](#input\_log\_cloudwatch\_logs\_role\_arn) | Amazon Resource Name of the service role that AWS AppSync will assume to publish to Amazon CloudWatch logs in your account. | `string` | `null` | no | 164 | | [log\_exclude\_verbose\_content](#input\_log\_exclude\_verbose\_content) | Set to TRUE to exclude sections that contain information such as headers, context, and evaluated mapping templates, regardless of logging level. | `bool` | `false` | no | 165 | | [log\_field\_log\_level](#input\_log\_field\_log\_level) | Field logging level. Valid values: ALL, ERROR, NONE. | `string` | `null` | no | 166 | | [logging\_enabled](#input\_logging\_enabled) | Whether to enable Cloudwatch logging on GraphQL API | `bool` | `false` | no | 167 | | [logs\_role\_name](#input\_logs\_role\_name) | Name of IAM role to create for Cloudwatch logs | `string` | `null` | no | 168 | | [logs\_role\_tags](#input\_logs\_role\_tags) | Map of tags to add to Cloudwatch logs IAM role | `map(string)` | `{}` | no | 169 | | [name](#input\_name) | Name of GraphQL API | `string` | `""` | no | 170 | | [openid\_connect\_config](#input\_openid\_connect\_config) | Nested argument containing OpenID Connect configuration. | `map(string)` | `{}` | no | 171 | | [resolver\_caching\_ttl](#input\_resolver\_caching\_ttl) | Default caching TTL for resolvers when caching is enabled | `number` | `60` | no | 172 | | [resolvers](#input\_resolvers) | Map of resolvers to create | `any` | `{}` | no | 173 | | [schema](#input\_schema) | The schema definition, in GraphQL schema language format. Terraform cannot perform drift detection of this configuration. | `string` | `""` | no | 174 | | [tags](#input\_tags) | Map of tags to add to all GraphQL resources created by this module | `map(string)` | `{}` | no | 175 | | [user\_pool\_config](#input\_user\_pool\_config) | The Amazon Cognito User Pool configuration. | `map(string)` | `{}` | no | 176 | | [xray\_enabled](#input\_xray\_enabled) | Whether tracing with X-ray is enabled. | `bool` | `false` | no | 177 | 178 | ## Outputs 179 | 180 | | Name | Description | 181 | |------|-------------| 182 | | [appsync\_api\_key\_id](#output\_appsync\_api\_key\_id) | Map of API Key ID (Formatted as ApiId:Key) | 183 | | [appsync\_api\_key\_key](#output\_appsync\_api\_key\_key) | Map of API Keys | 184 | | [appsync\_datasource\_arn](#output\_appsync\_datasource\_arn) | Map of ARNs of datasources | 185 | | [appsync\_function\_arn](#output\_appsync\_function\_arn) | Map of ARNs of functions | 186 | | [appsync\_function\_function\_id](#output\_appsync\_function\_function\_id) | Map of function IDs of functions | 187 | | [appsync\_function\_id](#output\_appsync\_function\_id) | Map of IDs of functions | 188 | | [appsync\_graphql\_api\_arn](#output\_appsync\_graphql\_api\_arn) | ARN of GraphQL API | 189 | | [appsync\_graphql\_api\_fqdns](#output\_appsync\_graphql\_api\_fqdns) | Map of FQDNs associated with the API (no protocol and path) | 190 | | [appsync\_graphql\_api\_id](#output\_appsync\_graphql\_api\_id) | ID of GraphQL API | 191 | | [appsync\_graphql\_api\_uris](#output\_appsync\_graphql\_api\_uris) | Map of URIs associated with the API | 192 | | [appsync\_resolver\_arn](#output\_appsync\_resolver\_arn) | Map of ARNs of resolvers | 193 | 194 | 195 | ## Authors 196 | 197 | Module managed by [Anton Babenko](https://github.com/antonbabenko). Check out [serverless.tf](https://serverless.tf) to learn more about doing serverless with Terraform. 198 | 199 | Please reach out to [Betajob](https://www.betajob.com/) if you are looking for commercial support for your Terraform, AWS, or serverless project. 200 | 201 | ## License 202 | 203 | Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-appsync/tree/master/LICENSE) for full details. 204 | --------------------------------------------------------------------------------