From 3bb91bafd637f3fef1732f18135646e4571c1ae3 Mon Sep 17 00:00:00 2001 From: Alexios Polyzos <6726377+Dragotic@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:37:28 +0300 Subject: [PATCH] feat: Initial release of the Terraform AWS ververica-cloud modules --- .github/workflows/lock.yml | 21 +++ .github/workflows/pr-title.yml | 32 ++++ .github/workflows/release.yml | 35 ++++ .github/workflows/stale.yml | 32 ++++ .github/workflows/terraform.yml | 30 +++ .gitignore | 9 + .releaserc.json | 29 +++ LICENSE | 176 ++++++++++++++++++ README.md | 40 ++++ .../private-connection/complete/README.md | 32 ++++ examples/private-connection/complete/main.tf | 35 ++++ .../private-connection/complete/outputs.tf | 14 ++ .../private-connection/complete/variables.tf | 0 .../private-connection/complete/versions.tf | 10 + .../elasticache/README.md | 31 +++ .../vpc-endpoint-service/elasticache/main.tf | 29 +++ .../elasticache/outputs.tf | 3 + .../elasticache/variables.tf | 0 .../elasticache/versions.tf | 14 ++ examples/vpc-endpoint-service/msk/README.md | 36 ++++ examples/vpc-endpoint-service/msk/main.tf | 28 +++ examples/vpc-endpoint-service/msk/outputs.tf | 3 + .../vpc-endpoint-service/msk/variables.tf | 0 examples/vpc-endpoint-service/msk/versions.tf | 14 ++ examples/vpc-endpoint-service/rds/README.md | 31 +++ examples/vpc-endpoint-service/rds/main.tf | 25 +++ examples/vpc-endpoint-service/rds/outputs.tf | 3 + .../vpc-endpoint-service/rds/variables.tf | 0 examples/vpc-endpoint-service/rds/versions.tf | 14 ++ modules/private-connection/README.md | 71 +++++++ modules/private-connection/iam.tf | 154 +++++++++++++++ modules/private-connection/main.tf | 16 ++ modules/private-connection/outputs.tf | 28 +++ modules/private-connection/variables.tf | 118 ++++++++++++ modules/private-connection/versions.tf | 10 + modules/vpc-endpoint-service/README.md | 50 +++++ modules/vpc-endpoint-service/main.tf | 68 +++++++ modules/vpc-endpoint-service/outputs.tf | 7 + modules/vpc-endpoint-service/variables.tf | 56 ++++++ modules/vpc-endpoint-service/versions.tf | 14 ++ 40 files changed, 1318 insertions(+) create mode 100644 .github/workflows/lock.yml create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/terraform.yml create mode 100644 .gitignore create mode 100644 .releaserc.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 examples/private-connection/complete/README.md create mode 100644 examples/private-connection/complete/main.tf create mode 100644 examples/private-connection/complete/outputs.tf create mode 100644 examples/private-connection/complete/variables.tf create mode 100644 examples/private-connection/complete/versions.tf create mode 100644 examples/vpc-endpoint-service/elasticache/README.md create mode 100644 examples/vpc-endpoint-service/elasticache/main.tf create mode 100644 examples/vpc-endpoint-service/elasticache/outputs.tf create mode 100644 examples/vpc-endpoint-service/elasticache/variables.tf create mode 100644 examples/vpc-endpoint-service/elasticache/versions.tf create mode 100644 examples/vpc-endpoint-service/msk/README.md create mode 100644 examples/vpc-endpoint-service/msk/main.tf create mode 100644 examples/vpc-endpoint-service/msk/outputs.tf create mode 100644 examples/vpc-endpoint-service/msk/variables.tf create mode 100644 examples/vpc-endpoint-service/msk/versions.tf create mode 100644 examples/vpc-endpoint-service/rds/README.md create mode 100644 examples/vpc-endpoint-service/rds/main.tf create mode 100644 examples/vpc-endpoint-service/rds/outputs.tf create mode 100644 examples/vpc-endpoint-service/rds/variables.tf create mode 100644 examples/vpc-endpoint-service/rds/versions.tf create mode 100644 modules/private-connection/README.md create mode 100644 modules/private-connection/iam.tf create mode 100644 modules/private-connection/main.tf create mode 100644 modules/private-connection/outputs.tf create mode 100644 modules/private-connection/variables.tf create mode 100644 modules/private-connection/versions.tf create mode 100644 modules/vpc-endpoint-service/README.md create mode 100644 modules/vpc-endpoint-service/main.tf create mode 100644 modules/vpc-endpoint-service/outputs.tf create mode 100644 modules/vpc-endpoint-service/variables.tf create mode 100644 modules/vpc-endpoint-service/versions.tf diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..35e3606 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +name: 'Lock Issues and Pull Requests' + +on: + schedule: + - cron: '0 1 * * *' + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-comment: > + I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. + issue-inactive-days: '30' + pr-comment: > + I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. + pr-inactive-days: '30' diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..b8dc454 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,32 @@ +name: 'Validate PR title' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + fix + feat + docs + ci + chore + requireScope: false + subjectPattern: ^[A-Z].+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + starts with an uppercase character. + wip: true + validateSingleCommit: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c735cc5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: + - main + - master + paths: + - '**/*.tf' + - '.github/workflows/release.yml' + +jobs: + release: + name: Release + runs-on: ubuntu-latest + # Skip running release workflow on forks + if: github.repository_owner == 'ververica' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Release + uses: cycjimmy/semantic-release-action@v4 + with: + semantic_version: 18.0.0 + extra_plugins: | + @semantic-release/changelog@6.0.0 + @semantic-release/git@10.0.0 + conventional-changelog-conventionalcommits@4.6.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..6ccd0ed --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,32 @@ +name: 'Mark or close stale issues and PRs' +on: + schedule: + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + # Staling issues and PR's + days-before-stale: 30 + stale-issue-label: stale + stale-pr-label: stale + stale-issue-message: | + This issue has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this issue will be closed in 10 days + stale-pr-message: | + This PR has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this PR will be closed in 10 days + # Not stale if have this labels or part of milestone + exempt-issue-labels: bug,wip,on-hold + exempt-pr-labels: bug,wip,on-hold + exempt-all-milestones: true + # Close issue operations + # Label will be automatically removed if the issues are no longer closed nor locked. + days-before-close: 10 + delete-branch: true + close-issue-message: This issue was automatically closed because of stale in 10 days + close-pr-message: This PR was automatically closed because of stale in 10 days diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml new file mode 100644 index 0000000..59e45f1 --- /dev/null +++ b/.github/workflows/terraform.yml @@ -0,0 +1,30 @@ +name: Terraform + +on: + pull_request: + branches: + - main + +jobs: + terraform_fmt_check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 'v1.5.5' + + - name: Find changed files + id: changes + uses: tj-actions/changed-files@v41 + with: + files: | + **.tf + + - name: Terraform fmt check + run: | + terraform fmt -check ${{ steps.changes.outputs.all_changed_files }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e3633f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Local .terraform directories +**/.terraform* + +# .tfstate files +*.tfstate +*.tfstate.* + +**/.idea +**/*.iml diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..9d6b7b4 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,29 @@ +{ + "branches": [ + "main", + "master" + ], + "ci": false, + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/github", + { + "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", + "labels": false, + "releasedLabels": false + } + ] + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..073ba65 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Ververica Cloud AWS Private Connection Terraform Module + +[Ververica Cloud](https://ververica.cloud) offers the capability to establish private connections with AWS services. This set of modules is used to create VPC endpoint services and IAM Role on a user's AWS account that is later used by [Ververica Cloud](https://ververica.cloud) to allow accessing resources from Flink jobs in their AWS Account, like a RDS for MySQL or MSK. + +## Usage +`private-connection`: + +```hcl +module "private_connection" { + source = "ververica/ververica-cloud/aws//modules/private-connection" + + role_name = "VervericaCloudIAMRole" + ververica_cloud_workspace_id = "my-workspace-id" + enable_elasticache = true + endpoint_services = { + redis = { + vpc_id = "vpc-1234567890abcdefg" + create_security_group_rule = true + security_group_id = "sg-1234567890abcdefg" + port = 6379 + nodes = [ + # To get the ip you can use something like: dig +short + { + ip_address = "172.31.40.27" + dns_endpoint = "demo-cluster-1-0001-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + }, + { + ip_address = "172.31.11.25" + dns_endpoint = "demo-cluster-1-0002-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + } + ] + tags = { + Description = "Used for Ververica Cloud" + } + } + } +} +``` diff --git a/examples/private-connection/complete/README.md b/examples/private-connection/complete/README.md new file mode 100644 index 0000000..938aac1 --- /dev/null +++ b/examples/private-connection/complete/README.md @@ -0,0 +1,32 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [private\_connection](#module\_private\_connection) | ververica/ververica-cloud/aws//modules/private-connection | n/a | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [enabled\_policies](#output\_enabled\_policies) | The IAM policies that are enabled for the IAM Role | +| [iam\_role\_arn](#output\_iam\_role\_arn) | The ARN of the IAM Role | +| [iam\_role\_id](#output\_iam\_role\_id) | The name of the IAM Role | diff --git a/examples/private-connection/complete/main.tf b/examples/private-connection/complete/main.tf new file mode 100644 index 0000000..df87238 --- /dev/null +++ b/examples/private-connection/complete/main.tf @@ -0,0 +1,35 @@ +provider "aws" { + region = "eu-central-1" +} + +module "private_connection" { + source = "ververica/ververica-cloud/aws//modules/private-connection" + + role_name = "VervericaCloudIAMRole" + ververica_cloud_workspace_id = "my-workspace-id" + enable_elasticache = true + endpoint_services = { + redis = { + vpc_id = "vpc-1234567890abcdefg" + create_security_group_rule = true + security_group_id = "sg-1234567890abcdefg" + port = 6379 + nodes = [ + # To get the ip you can use something like: dig +short + { + ip_address = "172.31.40.27" + dns_endpoint = "demo-cluster-1-0001-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + }, + { + ip_address = "172.31.11.25" + dns_endpoint = "demo-cluster-1-0002-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + } + ] + tags = { + Description = "Used for Ververica Cloud" + } + } + } +} diff --git a/examples/private-connection/complete/outputs.tf b/examples/private-connection/complete/outputs.tf new file mode 100644 index 0000000..bfce15b --- /dev/null +++ b/examples/private-connection/complete/outputs.tf @@ -0,0 +1,14 @@ +output "iam_role_id" { + description = "The name of the IAM Role" + value = module.iam_role.iam_role_id +} + +output "iam_role_arn" { + description = "The ARN of the IAM Role" + value = module.iam_role.iam_role_arn +} + +output "enabled_policies" { + description = "The IAM policies that are enabled for the IAM Role" + value = module.iam_role.enabled_policies +} diff --git a/examples/private-connection/complete/variables.tf b/examples/private-connection/complete/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/private-connection/complete/versions.tf b/examples/private-connection/complete/versions.tf new file mode 100644 index 0000000..ddfcb0e --- /dev/null +++ b/examples/private-connection/complete/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} diff --git a/examples/vpc-endpoint-service/elasticache/README.md b/examples/vpc-endpoint-service/elasticache/README.md new file mode 100644 index 0000000..d8426f8 --- /dev/null +++ b/examples/vpc-endpoint-service/elasticache/README.md @@ -0,0 +1,31 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | 3.6.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [elasticache\_private\_connection](#module\_elasticache\_private\_connection) | ververica/ververica-cloud/aws//modules/vpc-endpoint-service | n/a | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [endpoints](#output\_endpoints) | n/a | diff --git a/examples/vpc-endpoint-service/elasticache/main.tf b/examples/vpc-endpoint-service/elasticache/main.tf new file mode 100644 index 0000000..8845f70 --- /dev/null +++ b/examples/vpc-endpoint-service/elasticache/main.tf @@ -0,0 +1,29 @@ +provider "aws" { + region = "eu-central-1" +} + + +module "elasticache_private_connection" { + source = "ververica/ververica-cloud/aws//modules/vpc-endpoint-service" + + vpc_id = "vpc-1234567890abcdefg" + create_security_group_rule = true + security_group_id = "sg-1234567890abcdefg" + port = 6379 + nodes = [ + # To get the ip you can use something like: dig +short + { + ip_address = "172.31.40.27" + dns_endpoint = "demo-cluster-1-0001-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + }, + { + ip_address = "172.31.11.25" + dns_endpoint = "demo-cluster-1-0002-001.abcdef.0001.euc1.cache.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + } + ] + tags = { + Description = "Used for Ververica Cloud" + } +} diff --git a/examples/vpc-endpoint-service/elasticache/outputs.tf b/examples/vpc-endpoint-service/elasticache/outputs.tf new file mode 100644 index 0000000..1c7ecae --- /dev/null +++ b/examples/vpc-endpoint-service/elasticache/outputs.tf @@ -0,0 +1,3 @@ +output "endpoints" { + value = module.elasticache_private_connection.endpoints +} diff --git a/examples/vpc-endpoint-service/elasticache/variables.tf b/examples/vpc-endpoint-service/elasticache/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/vpc-endpoint-service/elasticache/versions.tf b/examples/vpc-endpoint-service/elasticache/versions.tf new file mode 100644 index 0000000..0e61a29 --- /dev/null +++ b/examples/vpc-endpoint-service/elasticache/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + random = { + source = "hashicorp/random" + version = "3.6.0" + } + } +} diff --git a/examples/vpc-endpoint-service/msk/README.md b/examples/vpc-endpoint-service/msk/README.md new file mode 100644 index 0000000..6788a4b --- /dev/null +++ b/examples/vpc-endpoint-service/msk/README.md @@ -0,0 +1,36 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | 3.6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [msk\_private\_connection](#module\_msk\_private\_connection) | ververica/ververica-cloud/aws//modules/vpc-endpoint-service | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_msk_broker_nodes.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/msk_broker_nodes) | data source | +| [aws_msk_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/msk_cluster) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [endpoints](#output\_endpoints) | n/a | diff --git a/examples/vpc-endpoint-service/msk/main.tf b/examples/vpc-endpoint-service/msk/main.tf new file mode 100644 index 0000000..b84134a --- /dev/null +++ b/examples/vpc-endpoint-service/msk/main.tf @@ -0,0 +1,28 @@ +provider "aws" { + region = "eu-central-1" +} + +data "aws_msk_cluster" "this" { + cluster_name = "demo-cluster-1" +} + +data "aws_msk_broker_nodes" "this" { + cluster_arn = data.aws_msk_cluster.this.arn +} + +module "msk_private_connection" { + source = "ververica/ververica-cloud/aws//modules/vpc-endpoint-service" + + vpc_id = "vpc-1234567890abcdefg" + create_security_group_rule = true + security_group_id = "sg-1234567890abcdefg" + port = 9098 + nodes = [for broker in data.aws_msk_broker_nodes.this.node_info_list : { + ip_address = broker.client_vpc_ip_address + dns_endpoint = one(broker.endpoints) + subnet_id = broker.client_subnet + }] + tags = { + Description = "Used for Ververica Cloud" + } +} diff --git a/examples/vpc-endpoint-service/msk/outputs.tf b/examples/vpc-endpoint-service/msk/outputs.tf new file mode 100644 index 0000000..0ede8ef --- /dev/null +++ b/examples/vpc-endpoint-service/msk/outputs.tf @@ -0,0 +1,3 @@ +output "endpoints" { + value = module.msk_private_connection.endpoints +} diff --git a/examples/vpc-endpoint-service/msk/variables.tf b/examples/vpc-endpoint-service/msk/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/vpc-endpoint-service/msk/versions.tf b/examples/vpc-endpoint-service/msk/versions.tf new file mode 100644 index 0000000..0e61a29 --- /dev/null +++ b/examples/vpc-endpoint-service/msk/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + random = { + source = "hashicorp/random" + version = "3.6.0" + } + } +} diff --git a/examples/vpc-endpoint-service/rds/README.md b/examples/vpc-endpoint-service/rds/README.md new file mode 100644 index 0000000..d8e254e --- /dev/null +++ b/examples/vpc-endpoint-service/rds/README.md @@ -0,0 +1,31 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | 3.6.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [rds\_private\_connection](#module\_rds\_private\_connection) | ververica/ververica-cloud/aws//modules/vpc-endpoint-service | n/a | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [endpoints](#output\_endpoints) | n/a | diff --git a/examples/vpc-endpoint-service/rds/main.tf b/examples/vpc-endpoint-service/rds/main.tf new file mode 100644 index 0000000..9aa2494 --- /dev/null +++ b/examples/vpc-endpoint-service/rds/main.tf @@ -0,0 +1,25 @@ +provider "aws" { + region = "eu-central-1" +} + + +module "rds_private_connection" { + source = "ververica/ververica-cloud/aws//modules/vpc-endpoint-service" + + vpc_id = "vpc-1234567890abcdefg" + create_security_group_rule = true + security_group_id = "sg-1234567890abcdefg" + port = 3306 + nodes = [ + { + # You need to use the instance endpoint of the Write instance and its subnet. + # To get the ip you can use something like: dig +short + ip_address = "172.31.40.27" + dns_endpoint = "demo-database-1-instance-1.abcdefg8ek.0001.eu-central-1.rds.amazonaws.com" + subnet_id = "subnet-1234567890abcdefg" + } + ] + tags = { + Description = "Used for Ververica Cloud" + } +} diff --git a/examples/vpc-endpoint-service/rds/outputs.tf b/examples/vpc-endpoint-service/rds/outputs.tf new file mode 100644 index 0000000..05b5551 --- /dev/null +++ b/examples/vpc-endpoint-service/rds/outputs.tf @@ -0,0 +1,3 @@ +output "endpoints" { + value = module.rds_private_connection.endpoints +} diff --git a/examples/vpc-endpoint-service/rds/variables.tf b/examples/vpc-endpoint-service/rds/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/vpc-endpoint-service/rds/versions.tf b/examples/vpc-endpoint-service/rds/versions.tf new file mode 100644 index 0000000..0e61a29 --- /dev/null +++ b/examples/vpc-endpoint-service/rds/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + random = { + source = "hashicorp/random" + version = "3.6.0" + } + } +} diff --git a/modules/private-connection/README.md b/modules/private-connection/README.md new file mode 100644 index 0000000..5ba29cd --- /dev/null +++ b/modules/private-connection/README.md @@ -0,0 +1,71 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [vpc\_endpoint\_service](#module\_vpc\_endpoint\_service) | ../vpc-endpoint-service | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_role.ververica_cloud_iam_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.kinesis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.msk](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.private_connection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_policy_document.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.kinesis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.msk](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.private_connection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.trust_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [dynamodb\_table\_arns](#input\_dynamodb\_table\_arns) | A list of DynamoDB Table ARNs that Ververica Cloud will have access to | `list(string)` | `null` | no | +| [enable\_dynamodb](#input\_enable\_dynamodb) | Enable the DynamoDB IAM Policies | `bool` | `false` | no | +| [enable\_elasticache](#input\_enable\_elasticache) | Enable the Elasticache IAM Policies | `bool` | `false` | no | +| [enable\_kinesis](#input\_enable\_kinesis) | Enable the Kinesis IAM Policies | `bool` | `false` | no | +| [enable\_msk](#input\_enable\_msk) | Enable the MSK IAM Policies | `bool` | `false` | no | +| [enable\_private\_connection](#input\_enable\_private\_connection) | Enable the VPC Endpoint IAM Policies | `bool` | `false` | no | +| [enable\_s3](#input\_enable\_s3) | Enable the S3 IAM Policies | `bool` | `false` | no | +| [endpoint\_services](#input\_endpoint\_services) | A map of maps containing VPC Endpoint Service configuration. Can contain 0..N maps of VPC Endpoint Services. | `any` | n/a | yes | +| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | +| [kinesis\_stream\_arns](#input\_kinesis\_stream\_arns) | A list of Kinesis Stream ARNs that Ververica Cloud will have access to | `list(string)` | `null` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `43200` | no | +| [role\_description](#input\_role\_description) | IAM Role description | `string` | `"The IAM Role used with Ververica Cloud to access various AWS Services in this account"` | no | +| [role\_name](#input\_role\_name) | The name for the IAM role to be created in your AWS Account. | `string` | `null` | no | +| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | +| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | +| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `""` | no | +| [s3\_bucket\_arns](#input\_s3\_bucket\_arns) | A list of S3 Bucket ARNs that Ververica Cloud will have access to | `list(string)` | `null` | no | +| [tags](#input\_tags) | A map of tags to add the the IAM role | `map(any)` | `{}` | no | +| [ververica\_cloud\_aws\_account](#input\_ververica\_cloud\_aws\_account) | The AWS Account ID of Ververica Cloud | `string` | `"794031221915"` | no | +| [ververica\_cloud\_workspace\_id](#input\_ververica\_cloud\_workspace\_id) | The Ververica Cloud Workspace ID | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [enabled\_policies](#output\_enabled\_policies) | The IAM policies that are enabled for the IAM Role | +| [endpoints](#output\_endpoints) | The VPC Endpoint Services that Ververica Cloud can have access to. Contains the Service Name and Endpoint for ease of input to Ververica Cloud Private Connection UI. | +| [iam\_role\_arn](#output\_iam\_role\_arn) | The ARN of the IAM Role | +| [iam\_role\_id](#output\_iam\_role\_id) | The name of the IAM Role | diff --git a/modules/private-connection/iam.tf b/modules/private-connection/iam.tf new file mode 100644 index 0000000..f3b06b8 --- /dev/null +++ b/modules/private-connection/iam.tf @@ -0,0 +1,154 @@ +locals { + enable_private_connection = var.enable_elasticache || var.enable_msk || var.enable_private_connection +} + +########################################################### +## Private Connection IAM Role with Policies ## +########################################################### +data "aws_iam_policy_document" "trust_policy" { + statement { + sid = "VervericaCloudAssumeRole" + effect = "Allow" + actions = ["sts:AssumeRole"] + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${var.ververica_cloud_aws_account}:root"] + } + condition { + test = "StringEquals" + variable = "sts:ExternalId" + values = [var.ververica_cloud_workspace_id] + } + } +} + +resource "aws_iam_role" "ververica_cloud_iam_role" { + name = var.role_name + name_prefix = var.role_name_prefix + path = var.role_path + description = var.role_description + force_detach_policies = var.force_detach_policies + max_session_duration = var.max_session_duration + permissions_boundary = var.role_permissions_boundary_arn + assume_role_policy = data.aws_iam_policy_document.trust_policy.json + +} + +data "aws_iam_policy_document" "kinesis" { + count = var.enable_kinesis ? 1 : 0 + statement { + sid = "KinesisPolicy" + effect = "Allow" + actions = ["kinesis:*"] + resources = var.kinesis_stream_arns == null ? ["*"] : var.kinesis_stream_arns + } +} + +resource "aws_iam_role_policy" "kinesis" { + count = var.enable_kinesis ? 1 : 0 + name = "VervericaCloud-Kinesis-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.kinesis[0].json +} + +data "aws_iam_policy_document" "dynamodb" { + count = var.enable_dynamodb ? 1 : 0 + statement { + sid = "DynamoDBPolicy" + effect = "Allow" + actions = ["dynamodb:*"] + resources = var.dynamodb_table_arns == null ? ["*"] : var.dynamodb_table_arns + } +} + +resource "aws_iam_role_policy" "dynamodb" { + count = var.enable_dynamodb ? 1 : 0 + name = "VervericaCloud-DynamoDB-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.dynamodb[0].json +} + +data "aws_iam_policy_document" "s3" { + count = var.enable_s3 ? 1 : 0 + statement { + sid = "S3BucketListPolicy" + effect = "Allow" + actions = ["s3:ListBucket"] + resources = var.s3_bucket_arns == null ? ["*"] : var.s3_bucket_arns + } + statement { + sid = "S3BucketRWPolicy" + effect = "Allow" + actions = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject" + ] + resources = var.s3_bucket_arns == null ? ["*"] : [for bucket in var.s3_bucket_arns : "${bucket}/*"] + } +} + +resource "aws_iam_role_policy" "s3" { + count = var.enable_s3 ? 1 : 0 + name = "VervericaCloud-S3-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.s3[0].json +} + +data "aws_iam_policy_document" "elasticache" { + count = var.enable_elasticache ? 1 : 0 + statement { + sid = "ElasticachePolicy" + effect = "Allow" + actions = [ + "elasticache:*", + ] + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "elasticache" { + count = var.enable_elasticache ? 1 : 0 + name = "VervericaCloud-Elasticache-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.elasticache[0].json +} + +data "aws_iam_policy_document" "msk" { + count = var.enable_msk ? 1 : 0 + statement { + sid = "MSKPolicy" + effect = "Allow" + actions = [ + "kafka-cluster:*", + ] + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "msk" { + count = var.enable_msk ? 1 : 0 + name = "VervericaCloud-MSK-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.msk[0].json +} + +data "aws_iam_policy_document" "private_connection" { + count = local.enable_private_connection ? 1 : 0 + statement { + sid = "AcceptVpcEndpointConnectionPolicy" + effect = "Allow" + actions = [ + "ec2:AcceptVpcEndpointConnections", + "ec2:DescribeVpcEndpointServices" + ] + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "private_connection" { + count = local.enable_private_connection ? 1 : 0 + name = "VervericaCloud-PrivateConnection-InlinePolicy" + role = aws_iam_role.ververica_cloud_iam_role.id + policy = data.aws_iam_policy_document.private_connection[0].json +} diff --git a/modules/private-connection/main.tf b/modules/private-connection/main.tf new file mode 100644 index 0000000..d3dd051 --- /dev/null +++ b/modules/private-connection/main.tf @@ -0,0 +1,16 @@ +########################################################### +## Private Connections with VPC Endpoint Services ## +########################################################### +module "vpc_endpoint_service" { + for_each = var.endpoint_services + source = "../vpc-endpoint-service" + + create_security_group_rule = lookup(each.value, "create_security_group_rule", false) + security_group_rule_description = lookup(each.value, "security_group_rule_description", "Allow access from Ververica Cloud") + security_group_id = lookup(each.value, "security_group_id", null) + vpc_id = lookup(each.value, "vpc_id", null) + port = lookup(each.value, "port", null) + nodes = lookup(each.value, "nodes", null) + principal_arn = lookup(each.value, "principal_arn", "arn:aws:iam::794031221915:root") + tags = lookup(each.value, "tags", null) +} diff --git a/modules/private-connection/outputs.tf b/modules/private-connection/outputs.tf new file mode 100644 index 0000000..5af8d43 --- /dev/null +++ b/modules/private-connection/outputs.tf @@ -0,0 +1,28 @@ +output "iam_role_id" { + description = "The name of the IAM Role" + value = aws_iam_role.ververica_cloud_iam_role.id +} + +output "iam_role_arn" { + description = "The ARN of the IAM Role" + value = aws_iam_role.ververica_cloud_iam_role.arn +} + +output "enabled_policies" { + description = "The IAM policies that are enabled for the IAM Role" + value = { + Kinesis = var.enable_kinesis + DynamoDB = var.enable_dynamodb + S3 = var.enable_s3 + Elasticache = var.enable_elasticache + MSK = var.enable_msk + PrivateConnection = local.enable_private_connection + } +} + +output "endpoints" { + description = "The VPC Endpoint Services that Ververica Cloud can have access to. Contains the Service Name and Endpoint for ease of input to Ververica Cloud Private Connection UI." + value = { + for k, endpoint in module.vpc_endpoint_service : k => endpoint.endpoints + } +} diff --git a/modules/private-connection/variables.tf b/modules/private-connection/variables.tf new file mode 100644 index 0000000..69043ad --- /dev/null +++ b/modules/private-connection/variables.tf @@ -0,0 +1,118 @@ +variable "ververica_cloud_aws_account" { + description = "The AWS Account ID of Ververica Cloud" + type = string + default = "794031221915" +} + +variable "ververica_cloud_workspace_id" { + description = "The Ververica Cloud Workspace ID" + type = string +} + +variable "role_name" { + description = "The name for the IAM role to be created in your AWS Account." + type = string + default = null +} + +variable "role_path" { + description = "Path of IAM role" + type = string + default = "/" +} + +variable "role_permissions_boundary_arn" { + description = "Permissions boundary ARN to use for IAM role" + type = string + default = "" +} + +variable "role_description" { + description = "IAM Role description" + type = string + default = "The IAM Role used with Ververica Cloud to access various AWS Services in this account" +} + +variable "role_name_prefix" { + description = "IAM role name prefix" + type = string + default = null +} + +variable "force_detach_policies" { + description = "Whether policies should be detached from this role when destroying" + type = bool + default = false +} + +variable "max_session_duration" { + description = "Maximum CLI/API session duration in seconds between 3600 and 43200" + type = number + default = 43200 +} + +variable "tags" { + description = "A map of tags to add the the IAM role" + type = map(any) + default = {} +} + +variable "enable_kinesis" { + description = "Enable the Kinesis IAM Policies" + type = bool + default = false +} + +variable "kinesis_stream_arns" { + description = "A list of Kinesis Stream ARNs that Ververica Cloud will have access to" + type = list(string) + default = null +} + +variable "enable_dynamodb" { + description = "Enable the DynamoDB IAM Policies" + type = bool + default = false +} + +variable "dynamodb_table_arns" { + description = "A list of DynamoDB Table ARNs that Ververica Cloud will have access to" + type = list(string) + default = null +} + +variable "enable_s3" { + description = "Enable the S3 IAM Policies" + type = bool + default = false +} + +variable "s3_bucket_arns" { + description = "A list of S3 Bucket ARNs that Ververica Cloud will have access to" + type = list(string) + default = null +} + +variable "enable_elasticache" { + description = "Enable the Elasticache IAM Policies" + type = bool + default = false +} + +variable "enable_msk" { + description = "Enable the MSK IAM Policies" + type = bool + default = false +} + +variable "enable_private_connection" { + description = "Enable the VPC Endpoint IAM Policies" + type = bool + default = false +} + +variable "endpoint_services" { + description = "A map of maps containing VPC Endpoint Service configuration. Can contain 0..N maps of VPC Endpoint Services." + type = any +} + diff --git a/modules/private-connection/versions.tf b/modules/private-connection/versions.tf new file mode 100644 index 0000000..ddfcb0e --- /dev/null +++ b/modules/private-connection/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} diff --git a/modules/vpc-endpoint-service/README.md b/modules/vpc-endpoint-service/README.md new file mode 100644 index 0000000..2738d2f --- /dev/null +++ b/modules/vpc-endpoint-service/README.md @@ -0,0 +1,50 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | 3.6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.0 | +| [random](#provider\_random) | 3.6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource | +| [aws_lb_listener.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | +| [aws_lb_target_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource | +| [aws_lb_target_group_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment) | resource | +| [aws_security_group_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_vpc_endpoint_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint_service) | resource | +| [aws_vpc_endpoint_service_allowed_principal.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint_service_allowed_principal) | resource | +| [random_string.this](https://registry.terraform.io/providers/hashicorp/random/3.6.0/docs/resources/string) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [create\_security\_group\_rule](#input\_create\_security\_group\_rule) | Whether to add the security group rule or not | `bool` | `false` | no | +| [nodes](#input\_nodes) | A list of objects containing DNS endpoint, IP Address, and Subnet ID for the Service that we create the resources. |
list(object({
dns_endpoint = string
ip_address = string
subnet_id = string
}))
| n/a | yes | +| [port](#input\_port) | The port that Ververica Cloud is going to use to access your service. eg Elasticache is 6379, MSK with IAM is 9098, etc. | `number` | n/a | yes | +| [principal\_arn](#input\_principal\_arn) | The principal ARN that will have access to the Endpoint Service. Default value is the Ververica Cloud AWS Account ID | `string` | `"arn:aws:iam::794031221915:root"` | no | +| [security\_group\_id](#input\_security\_group\_id) | The security group ID to which the module adds a new rule. | `string` | `""` | no | +| [security\_group\_rule\_description](#input\_security\_group\_rule\_description) | A description for the security group rule | `string` | `"Allow access from Ververica Cloud"` | no | +| [tags](#input\_tags) | A map of tags to add to the resources. | `map(string)` | `{}` | no | +| [vpc\_id](#input\_vpc\_id) | The VPC ID where the AWS service exists and where the resources will be created. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [endpoints](#output\_endpoints) | The VPC Endpoint Services that Ververica Cloud can have access to. Contains the Service Name and Endpoint for ease of input to Ververica Cloud Private Connection UI. | diff --git a/modules/vpc-endpoint-service/main.tf b/modules/vpc-endpoint-service/main.tf new file mode 100644 index 0000000..b974b3c --- /dev/null +++ b/modules/vpc-endpoint-service/main.tf @@ -0,0 +1,68 @@ +resource "random_string" "this" { + length = 6 + special = false +} + +resource "aws_security_group_rule" "this" { + count = var.create_security_group_rule ? 1 : 0 + description = var.security_group_rule_description + type = "ingress" + from_port = var.port + to_port = var.port + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = var.security_group_id +} + +resource "aws_lb_target_group" "this" { + count = length(var.nodes) + name = "node-lb-target-group-${count.index}-${random_string.this.result}" + port = var.port + protocol = "TCP" + target_type = "ip" + vpc_id = var.vpc_id + tags = var.tags +} + +resource "aws_lb_target_group_attachment" "this" { + count = length(var.nodes) + target_group_arn = aws_lb_target_group.this[count.index].arn + target_id = var.nodes[count.index].ip_address + port = var.port +} + +resource "aws_lb" "this" { + count = length(var.nodes) + name = "node-lb-${count.index}-${random_string.this.result}" + internal = true + load_balancer_type = "network" + subnets = [var.nodes[count.index].subnet_id] + enable_cross_zone_load_balancing = false + tags = var.tags +} + +resource "aws_lb_listener" "this" { + count = length(var.nodes) + load_balancer_arn = aws_lb.this[count.index].arn + port = var.port + protocol = "TCP" + tags = var.tags + + default_action { + target_group_arn = aws_lb_target_group.this[count.index].arn + type = "forward" + } +} + +resource "aws_vpc_endpoint_service" "this" { + count = length(var.nodes) + acceptance_required = true + network_load_balancer_arns = [aws_lb.this[count.index].arn] + tags = var.tags +} + +resource "aws_vpc_endpoint_service_allowed_principal" "this" { + count = length(var.nodes) + principal_arn = var.principal_arn + vpc_endpoint_service_id = aws_vpc_endpoint_service.this[count.index].id +} diff --git a/modules/vpc-endpoint-service/outputs.tf b/modules/vpc-endpoint-service/outputs.tf new file mode 100644 index 0000000..de6586c --- /dev/null +++ b/modules/vpc-endpoint-service/outputs.tf @@ -0,0 +1,7 @@ +output "endpoints" { + description = "The VPC Endpoint Services that Ververica Cloud can have access to. Contains the Service Name and Endpoint for ease of input to Ververica Cloud Private Connection UI." + value = [for idx, endpoint in aws_vpc_endpoint_service.this : { + service_name = endpoint.service_name + endpoint = var.nodes[idx].dns_endpoint + }] +} diff --git a/modules/vpc-endpoint-service/variables.tf b/modules/vpc-endpoint-service/variables.tf new file mode 100644 index 0000000..4fbef5b --- /dev/null +++ b/modules/vpc-endpoint-service/variables.tf @@ -0,0 +1,56 @@ +variable "create_security_group_rule" { + description = "Whether to add the security group rule or not" + type = bool + default = false +} + +variable "security_group_rule_description" { + description = "A description for the security group rule" + type = string + default = "Allow access from Ververica Cloud" +} + +variable "security_group_id" { + description = "The security group ID to which the module adds a new rule." + type = string + default = "" +} + +variable "port" { + description = "The port that Ververica Cloud is going to use to access your service. eg Elasticache is 6379, MSK with IAM is 9098, etc." + type = number +} + +variable "principal_arn" { + description = "The principal ARN that will have access to the Endpoint Service. Default value is the Ververica Cloud AWS Account ID" + type = string + default = "arn:aws:iam::794031221915:root" +} + +variable "vpc_id" { + description = "The VPC ID where the AWS service exists and where the resources will be created." + type = string +} + +variable "nodes" { + description = "A list of objects containing DNS endpoint, IP Address, and Subnet ID for the Service that we create the resources." + type = list(object({ + dns_endpoint = string + ip_address = string + subnet_id = string + })) + # Example + # [ + # { + # dns_endpoint = "b-1.democluster1.abcdef.c1.kafka.eu-central-1.amazonaws.com" + # ip_address = "172.31.40.27" + # subnet_id = "subnet-14cf73a7634ac123" + # } + # ] +} + +variable "tags" { + description = "A map of tags to add to the resources." + type = map(string) + default = {} +} diff --git a/modules/vpc-endpoint-service/versions.tf b/modules/vpc-endpoint-service/versions.tf new file mode 100644 index 0000000..0e61a29 --- /dev/null +++ b/modules/vpc-endpoint-service/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + random = { + source = "hashicorp/random" + version = "3.6.0" + } + } +}