├── .editorconfig ├── .github └── workflows │ ├── lock.yml │ ├── pr-title.yml │ ├── pre-commit.yml │ ├── release.yml │ └── stale-actions.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .releaserc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── README.md └── complete │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf /.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 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 31 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '50 1 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v5 12 | with: 13 | github-token: ${{ secrets.GITHUB_TOKEN }} 14 | issue-comment: > 15 | 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. 16 | 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. 17 | issue-inactive-days: '30' 18 | pr-comment: > 19 | 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. 20 | 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. 21 | pr-inactive-days: '30' 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-title.yml: -------------------------------------------------------------------------------- 1 | name: 'Validate PR title' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Please look up the latest version from 16 | # https://github.com/amannn/action-semantic-pull-request/releases 17 | - uses: amannn/action-semantic-pull-request@v5.5.3 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | # Configure which types are allowed. 22 | # Default: https://github.com/commitizen/conventional-commit-types 23 | types: | 24 | fix 25 | feat 26 | docs 27 | ci 28 | chore 29 | # Configure that a scope must always be provided. 30 | requireScope: false 31 | # Configure additional validation for the subject based on a regex. 32 | # This example ensures the subject starts with an uppercase character. 33 | subjectPattern: ^[A-Z].+$ 34 | # If `subjectPattern` is configured, you can use this property to override 35 | # the default error message that is shown when the pattern doesn't match. 36 | # The variables `subject` and `title` can be used within the message. 37 | subjectPatternError: | 38 | The subject "{subject}" found in the pull request title "{title}" 39 | didn't match the configured pattern. Please ensure that the subject 40 | starts with an uppercase character. 41 | # For work-in-progress PRs you can typically use draft pull requests 42 | # from Github. However, private repositories on the free plan don't have 43 | # this option and therefore this action allows you to opt-in to using the 44 | # special "[WIP]" prefix to indicate this state. This will avoid the 45 | # validation of the PR title and the pull request checks remain pending. 46 | # Note that a second check will be reported if this is enabled. 47 | wip: true 48 | # When using "Squash and merge" on a PR with only one commit, GitHub 49 | # will suggest using that commit message instead of the PR title for the 50 | # merge commit, and it's easy to commit this by mistake. Enable this option 51 | # to also validate the commit message for one commit PRs. 52 | validateSingleCommit: false 53 | -------------------------------------------------------------------------------- /.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.19.0 11 | TFLINT_VERSION: v0.53.0 12 | 13 | jobs: 14 | collectInputs: 15 | name: Collect workflow inputs 16 | runs-on: ubuntu-latest 17 | outputs: 18 | directories: ${{ steps.dirs.outputs.directories }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Get root directories 24 | id: dirs 25 | uses: clowdhaus/terraform-composite-actions/directories@v1.9.0 26 | 27 | preCommitMinVersions: 28 | name: Min TF pre-commit 29 | needs: collectInputs 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} 34 | steps: 35 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 36 | - name: Delete huge unnecessary tools folder 37 | run: | 38 | rm -rf /opt/hostedtoolcache/CodeQL 39 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 40 | rm -rf /opt/hostedtoolcache/Ruby 41 | rm -rf /opt/hostedtoolcache/go 42 | 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | 46 | - name: Terraform min/max versions 47 | id: minMax 48 | uses: clowdhaus/terraform-min-max@v1.3.1 49 | with: 50 | directory: ${{ matrix.directory }} 51 | 52 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 53 | # Run only validate pre-commit check on min version supported 54 | if: ${{ matrix.directory != '.' }} 55 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 56 | with: 57 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 58 | tflint-version: ${{ env.TFLINT_VERSION }} 59 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' 60 | 61 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 62 | # Run only validate pre-commit check on min version supported 63 | if: ${{ matrix.directory == '.' }} 64 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 65 | with: 66 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 67 | tflint-version: ${{ env.TFLINT_VERSION }} 68 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' 69 | 70 | preCommitMaxVersion: 71 | name: Max TF pre-commit 72 | runs-on: ubuntu-latest 73 | needs: collectInputs 74 | steps: 75 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 76 | - name: Delete huge unnecessary tools folder 77 | run: | 78 | rm -rf /opt/hostedtoolcache/CodeQL 79 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 80 | rm -rf /opt/hostedtoolcache/Ruby 81 | rm -rf /opt/hostedtoolcache/go 82 | 83 | - name: Checkout 84 | uses: actions/checkout@v4 85 | with: 86 | ref: ${{ github.event.pull_request.head.ref }} 87 | repository: ${{github.event.pull_request.head.repo.full_name}} 88 | 89 | - name: Terraform min/max versions 90 | id: minMax 91 | uses: clowdhaus/terraform-min-max@v1.3.1 92 | 93 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} 94 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 95 | with: 96 | terraform-version: ${{ steps.minMax.outputs.maxVersion }} 97 | tflint-version: ${{ env.TFLINT_VERSION }} 98 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} 99 | install-hcledit: true 100 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths: 10 | - '**/*.tpl' 11 | - '**/*.py' 12 | - '**/*.tf' 13 | - '.github/workflows/release.yml' 14 | 15 | jobs: 16 | release: 17 | name: Release 18 | runs-on: ubuntu-latest 19 | # Skip running release workflow on forks 20 | if: github.repository_owner == 'terraform-aws-modules' 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | persist-credentials: false 26 | fetch-depth: 0 27 | 28 | - name: Release 29 | uses: cycjimmy/semantic-release-action@v4 30 | with: 31 | semantic_version: 23.0.2 32 | extra_plugins: | 33 | @semantic-release/changelog@6.0.3 34 | @semantic-release/git@10.0.1 35 | conventional-changelog-conventionalcommits@7.0.2 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/stale-actions.yaml: -------------------------------------------------------------------------------- 1 | name: 'Mark or close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | # Staling issues and PR's 14 | days-before-stale: 30 15 | stale-issue-label: stale 16 | stale-pr-label: stale 17 | stale-issue-message: | 18 | This issue has been automatically marked as stale because it has been open 30 days 19 | with no activity. Remove stale label or comment or this issue will be closed in 10 days 20 | stale-pr-message: | 21 | This PR has been automatically marked as stale because it has been open 30 days 22 | with no activity. Remove stale label or comment or this PR will be closed in 10 days 23 | # Not stale if have this labels or part of milestone 24 | exempt-issue-labels: bug,wip,on-hold 25 | exempt-pr-labels: bug,wip,on-hold 26 | exempt-all-milestones: true 27 | # Close issue operations 28 | # Label will be automatically removed if the issues are no longer closed nor locked. 29 | days-before-close: 10 30 | delete-branch: true 31 | close-issue-message: This issue was automatically closed because of stale in 10 days 32 | close-pr-message: This PR was automatically closed because of stale in 10 days 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # terraform lockfile 9 | .terraform.lock.hcl 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 15 | # password, private keys, and other secrets. These should not be part of version 16 | # control as they are data points which are potentially sensitive and subject 17 | # to change depending on the environment. 18 | # 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 | # Include override files you do wish to add to version control using negated pattern 29 | # 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.96.1 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_docs 7 | args: 8 | - '--args=--lockfile=false' 9 | - id: terraform_tflint 10 | args: 11 | - '--args=--only=terraform_deprecated_interpolation' 12 | - '--args=--only=terraform_deprecated_index' 13 | - '--args=--only=terraform_unused_declarations' 14 | - '--args=--only=terraform_comment_syntax' 15 | - '--args=--only=terraform_documented_outputs' 16 | - '--args=--only=terraform_documented_variables' 17 | - '--args=--only=terraform_typed_variables' 18 | - '--args=--only=terraform_module_pinned_source' 19 | - '--args=--only=terraform_naming_convention' 20 | - '--args=--only=terraform_required_version' 21 | - '--args=--only=terraform_required_providers' 22 | - '--args=--only=terraform_standard_module_structure' 23 | - '--args=--only=terraform_workspace_remote' 24 | - id: terraform_validate 25 | - repo: https://github.com/pre-commit/pre-commit-hooks 26 | rev: v5.0.0 27 | hooks: 28 | - id: check-merge-conflict 29 | - id: end-of-file-fixer 30 | - id: trailing-whitespace 31 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "ci": false, 7 | "plugins": [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "conventionalcommits" 12 | } 13 | ], 14 | [ 15 | "@semantic-release/release-notes-generator", 16 | { 17 | "preset": "conventionalcommits" 18 | } 19 | ], 20 | [ 21 | "@semantic-release/github", 22 | { 23 | "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", 24 | "labels": false, 25 | "releasedLabels": false 26 | } 27 | ], 28 | [ 29 | "@semantic-release/changelog", 30 | { 31 | "changelogFile": "CHANGELOG.md", 32 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." 33 | } 34 | ], 35 | [ 36 | "@semantic-release/git", 37 | { 38 | "assets": [ 39 | "CHANGELOG.md" 40 | ], 41 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 42 | } 43 | ] 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [1.2.1](https://github.com/terraform-aws-modules/terraform-aws-app-runner/compare/v1.2.0...v1.2.1) (2024-03-06) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * Update CI workflow versions to remove deprecated runtime warnings ([#12](https://github.com/terraform-aws-modules/terraform-aws-app-runner/issues/12)) ([1f4ac9c](https://github.com/terraform-aws-modules/terraform-aws-app-runner/commit/1f4ac9c46f2a1cda7845ed93e1bb4792e7d0e768)) 11 | 12 | ## [1.2.0](https://github.com/terraform-aws-modules/terraform-aws-app-runner/compare/v1.1.1...v1.2.0) (2023-03-07) 13 | 14 | 15 | ### Features 16 | 17 | * Add support for runtime_environment_secrets ([#5](https://github.com/terraform-aws-modules/terraform-aws-app-runner/issues/5)) ([1a159d7](https://github.com/terraform-aws-modules/terraform-aws-app-runner/commit/1a159d73c657b864eaf20e158db3dfb1f23a33ec)) 18 | 19 | ### [1.1.1](https://github.com/terraform-aws-modules/terraform-aws-app-runner/compare/v1.1.0...v1.1.1) (2023-02-01) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * Use a version for to avoid GitHub API rate limiting on CI workflows ([#3](https://github.com/terraform-aws-modules/terraform-aws-app-runner/issues/3)) ([ec37cc4](https://github.com/terraform-aws-modules/terraform-aws-app-runner/commit/ec37cc4868874c15f6f2f6ec1eaaacc8319bdc0a)) 25 | 26 | ## [1.1.0](https://github.com/terraform-aws-modules/terraform-aws-app-runner/compare/v1.0.1...v1.1.0) (2022-11-04) 27 | 28 | 29 | ### Features 30 | 31 | * Add support for creating private services with VPC ingress connection ([#2](https://github.com/terraform-aws-modules/terraform-aws-app-runner/issues/2)) ([214af5c](https://github.com/terraform-aws-modules/terraform-aws-app-runner/commit/214af5ceafc2f9c2f7edd1c91ffa076edd3075e0)) 32 | 33 | ### [1.0.1](https://github.com/terraform-aws-modules/terraform-aws-app-runner/compare/v1.0.0...v1.0.1) (2022-10-29) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * Update CI configuration files to use latest version ([#1](https://github.com/terraform-aws-modules/terraform-aws-app-runner/issues/1)) ([042ff02](https://github.com/terraform-aws-modules/terraform-aws-app-runner/commit/042ff024ea330a0af33807a9d2a24b3c9cf14eec)) 39 | 40 | ## 1.0.0 (2022-10-01) 41 | 42 | 43 | ### Features 44 | 45 | * add cloudwatch logs and IAM roles, update example ([e58982d](https://github.com/clowdhaus/terraform-aws-app-runner/commit/e58982df317e67302dccb22a18bc027f47965208)) 46 | * Add X-Ray policy, VPC connectory security group, and ability to toggle resources independently ([bb47aa9](https://github.com/clowdhaus/terraform-aws-app-runner/commit/bb47aa9289e1debfbd579fa366f2966c15521068)) 47 | -------------------------------------------------------------------------------- /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 App Runner Terraform module 2 | 3 | Terraform module which creates AWS App Runner resources. 4 | 5 | [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) 6 | 7 | ## Usage 8 | 9 | See [`examples`](https://github.com/terraform-aws-modules/terraform-aws-app-runner/tree/master/examples) directory for working examples to reference: 10 | 11 | ### AppRunner Common/Shared Configurations 12 | 13 | ```hcl 14 | module "app_runner_shared_configs" { 15 | source = "terraform-aws-modules/app-runner/aws" 16 | 17 | # Disable service resources 18 | create_service = false 19 | 20 | connections = { 21 | # The AWS Connector for GitHub connects to your GitHub account is a one-time setup, 22 | # You can reuse the connection for creating multiple App Runner services based on repositories in this account. 23 | # After creation, you must complete the authentication handshake using the App Runner console. 24 | github = { 25 | provider_type = "GITHUB" 26 | } 27 | } 28 | 29 | auto_scaling_configurations = { 30 | mini = { 31 | name = "mini" 32 | max_concurrency = 20 33 | max_size = 5 34 | min_size = 1 35 | 36 | tags = { 37 | Type = "Mini" 38 | } 39 | } 40 | 41 | mega = { 42 | name = "mega" 43 | max_concurrency = 200 44 | max_size = 25 45 | min_size = 5 46 | 47 | tags = { 48 | Type = "MEGA" 49 | } 50 | } 51 | } 52 | 53 | tags = { 54 | Terraform = "true" 55 | Environment = "dev" 56 | } 57 | } 58 | ``` 59 | 60 | ### Code Based AppRunner Service 61 | 62 | ```hcl 63 | module "app_runner_code_base" { 64 | source = "terraform-aws-modules/app-runner/aws" 65 | 66 | service_name = "example-code-base" 67 | 68 | # From shared configs created above 69 | auto_scaling_configuration_arn = module.app_runner_shared_configs.auto_scaling_configurations["mini"].arn 70 | 71 | source_configuration = { 72 | authentication_configuration = { 73 | # From shared configs created above 74 | connection_arn = module.app_runner_shared_configs.connections["github"].arn 75 | } 76 | auto_deployments_enabled = false 77 | code_repository = { 78 | code_configuration = { 79 | configuration_source = "REPOSITORY" 80 | } 81 | repository_url = "https://github.com/aws-containers/hello-app-runner" 82 | source_code_version = { 83 | type = "BRANCH" 84 | value = "main" 85 | } 86 | } 87 | } 88 | 89 | tags = { 90 | Terraform = "true" 91 | Environment = "dev" 92 | } 93 | } 94 | ``` 95 | 96 | ### Image Based AppRunner Service 97 | 98 | ```hcl 99 | module "app_runner_image_base" { 100 | source = "terraform-aws-modules/app-runner/aws" 101 | 102 | service_name = "example-image-base" 103 | 104 | # From shared configs 105 | auto_scaling_configuration_arn = module.app_runner_shared_configs.auto_scaling_configurations["mega"].arn 106 | 107 | # IAM instance profile permissions to access secrets 108 | instance_policy_statements = { 109 | GetSecretValue = { 110 | actions = ["secretsmanager:GetSecretValue"] 111 | resources = [aws_secretsmanager_secret.this.arn] 112 | } 113 | } 114 | 115 | source_configuration = { 116 | auto_deployments_enabled = false 117 | image_repository = { 118 | image_configuration = { 119 | port = 8000 120 | runtime_environment_variables = { 121 | MY_VARIABLE = "hello!" 122 | } 123 | runtime_environment_secrets = { 124 | MY_SECRET = aws_secretsmanager_secret.this.arn 125 | } 126 | } 127 | image_identifier = "public.ecr.aws/aws-containers/hello-app-runner:latest" 128 | image_repository_type = "ECR_PUBLIC" 129 | } 130 | } 131 | 132 | create_vpc_connector = true 133 | vpc_connector_subnets = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] 134 | vpc_connector_security_groups = ["sg-12345678"] 135 | network_configuration = { 136 | egress_configuration = { 137 | egress_type = "VPC" 138 | } 139 | } 140 | 141 | enable_observability_configuration = true 142 | 143 | tags = { 144 | Terraform = "true" 145 | Environment = "dev" 146 | } 147 | } 148 | ``` 149 | 150 | ### Private AppRunner Service 151 | 152 | ```hcl 153 | module "app_runner_private" { 154 | source = "terraform-aws-modules/app-runner/aws" 155 | 156 | service_name = "example-private" 157 | 158 | ... 159 | 160 | # Ingress 161 | create_ingress_vpc_connection = true 162 | ingress_vpc_id = "vpc-12345678" 163 | ingress_vpc_endpoint_id = "vpce-01234567890123456 s" 164 | 165 | # Egress 166 | create_vpc_connector = true 167 | vpc_connector_subnets = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] 168 | vpc_connector_security_groups = ["sg-12345678"] 169 | 170 | network_configuration = { 171 | ingress_configuration = { 172 | is_publicly_accessible = false 173 | } 174 | egress_configuration = { 175 | egress_type = "VPC" 176 | } 177 | } 178 | 179 | tags = { 180 | Terraform = "true" 181 | Environment = "dev" 182 | } 183 | } 184 | ``` 185 | 186 | ## Examples 187 | 188 | Examples codified under the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-app-runner/tree/master/examples) are intended to give users references for how to use the module(s) as well as testing/validating changes to the source code of the module. If contributing to the project, please be sure to make any appropriate updates to the relevant examples to allow maintainers to test your changes and to keep the examples up to date for users. Thank you! 189 | 190 | - [Complete](https://github.com/terraform-aws-modules/terraform-aws-app-runner/tree/master/examples/complete) 191 | 192 | 193 | ## Requirements 194 | 195 | | Name | Version | 196 | |------|---------| 197 | | [terraform](#requirement\_terraform) | >= 1.0 | 198 | | [aws](#requirement\_aws) | >= 4.51 | 199 | 200 | ## Providers 201 | 202 | | Name | Version | 203 | |------|---------| 204 | | [aws](#provider\_aws) | >= 4.51 | 205 | 206 | ## Modules 207 | 208 | No modules. 209 | 210 | ## Resources 211 | 212 | | Name | Type | 213 | |------|------| 214 | | [aws_apprunner_auto_scaling_configuration_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_auto_scaling_configuration_version) | resource | 215 | | [aws_apprunner_connection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_connection) | resource | 216 | | [aws_apprunner_custom_domain_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_custom_domain_association) | resource | 217 | | [aws_apprunner_observability_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_observability_configuration) | resource | 218 | | [aws_apprunner_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_service) | resource | 219 | | [aws_apprunner_vpc_connector.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_vpc_connector) | resource | 220 | | [aws_apprunner_vpc_ingress_connection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apprunner_vpc_ingress_connection) | resource | 221 | | [aws_iam_policy.access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 222 | | [aws_iam_policy.instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 223 | | [aws_iam_role.access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 224 | | [aws_iam_role.instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 225 | | [aws_iam_role_policy_attachment.access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 226 | | [aws_iam_role_policy_attachment.access_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 227 | | [aws_iam_role_policy_attachment.instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 228 | | [aws_iam_role_policy_attachment.instance_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 229 | | [aws_iam_role_policy_attachment.instance_xray](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 230 | | [aws_iam_policy_document.access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 231 | | [aws_iam_policy_document.access_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 232 | | [aws_iam_policy_document.instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 233 | | [aws_iam_policy_document.instance_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 234 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 235 | 236 | ## Inputs 237 | 238 | | Name | Description | Type | Default | Required | 239 | |------|-------------|------|---------|:--------:| 240 | | [access\_iam\_role\_description](#input\_access\_iam\_role\_description) | Description of the role | `string` | `null` | no | 241 | | [access\_iam\_role\_name](#input\_access\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no | 242 | | [access\_iam\_role\_path](#input\_access\_iam\_role\_path) | IAM role path | `string` | `null` | no | 243 | | [access\_iam\_role\_permissions\_boundary](#input\_access\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | 244 | | [access\_iam\_role\_policies](#input\_access\_iam\_role\_policies) | IAM policies to attach to the IAM role | `map(string)` | `{}` | no | 245 | | [access\_iam\_role\_use\_name\_prefix](#input\_access\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no | 246 | | [auto\_scaling\_configuration\_arn](#input\_auto\_scaling\_configuration\_arn) | ARN of an App Runner automatic scaling configuration resource that you want to associate with your service. If not provided, App Runner associates the latest revision of a default auto scaling configuration | `string` | `null` | no | 247 | | [auto\_scaling\_configurations](#input\_auto\_scaling\_configurations) | Map of auto-scaling configuration definitions to create | `any` | `{}` | no | 248 | | [connections](#input\_connections) | Map of connection definitions to create | `any` | `{}` | no | 249 | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | 250 | | [create\_access\_iam\_role](#input\_create\_access\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `false` | no | 251 | | [create\_custom\_domain\_association](#input\_create\_custom\_domain\_association) | Determines whether a Custom Domain Association will be created | `bool` | `false` | no | 252 | | [create\_ingress\_vpc\_connection](#input\_create\_ingress\_vpc\_connection) | Determines whether a VPC ingress configuration will be created | `bool` | `false` | no | 253 | | [create\_instance\_iam\_role](#input\_create\_instance\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `true` | no | 254 | | [create\_service](#input\_create\_service) | Determines whether the service will be created | `bool` | `true` | no | 255 | | [create\_vpc\_connector](#input\_create\_vpc\_connector) | Determines whether a VPC Connector will be created | `bool` | `false` | no | 256 | | [domain\_name](#input\_domain\_name) | The custom domain endpoint to association. Specify a base domain e.g., `example.com` or a subdomain e.g., `subdomain.example.com` | `string` | `""` | no | 257 | | [enable\_observability\_configuration](#input\_enable\_observability\_configuration) | Determines whether an X-Ray Observability Configuration will be created and assigned to the service | `bool` | `true` | no | 258 | | [enable\_www\_subdomain](#input\_enable\_www\_subdomain) | Whether to associate the subdomain with the App Runner service in addition to the base domain. Defaults to `true` | `bool` | `null` | no | 259 | | [encryption\_configuration](#input\_encryption\_configuration) | The encryption configuration for the service | `any` | `{}` | no | 260 | | [health\_check\_configuration](#input\_health\_check\_configuration) | The health check configuration for the service | `any` | `{}` | no | 261 | | [ingress\_vpc\_endpoint\_id](#input\_ingress\_vpc\_endpoint\_id) | The ID of the VPC endpoint that is used for the VPC ingress configuration | `string` | `""` | no | 262 | | [ingress\_vpc\_id](#input\_ingress\_vpc\_id) | The ID of the VPC that is used for the VPC ingress configuration | `string` | `""` | no | 263 | | [instance\_configuration](#input\_instance\_configuration) | The instance configuration for the service | `any` | `{}` | no | 264 | | [instance\_iam\_role\_description](#input\_instance\_iam\_role\_description) | Description of the role | `string` | `null` | no | 265 | | [instance\_iam\_role\_name](#input\_instance\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no | 266 | | [instance\_iam\_role\_path](#input\_instance\_iam\_role\_path) | IAM role path | `string` | `null` | no | 267 | | [instance\_iam\_role\_permissions\_boundary](#input\_instance\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | 268 | | [instance\_iam\_role\_policies](#input\_instance\_iam\_role\_policies) | IAM policies to attach to the IAM role | `map(string)` | `{}` | no | 269 | | [instance\_iam\_role\_use\_name\_prefix](#input\_instance\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no | 270 | | [instance\_policy\_statements](#input\_instance\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | 271 | | [network\_configuration](#input\_network\_configuration) | The network configuration for the service | `any` | `{}` | no | 272 | | [observability\_configuration](#input\_observability\_configuration) | The observability configuration for the service | `any` | `{}` | no | 273 | | [private\_ecr\_arn](#input\_private\_ecr\_arn) | The ARN of the private ECR repository that contains the service image to launch | `string` | `null` | no | 274 | | [service\_name](#input\_service\_name) | The name of the service | `string` | `""` | no | 275 | | [source\_configuration](#input\_source\_configuration) | The source configuration for the service | `any` | `{}` | no | 276 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | 277 | | [vpc\_connector\_name](#input\_vpc\_connector\_name) | The name of the VPC Connector | `string` | `""` | no | 278 | | [vpc\_connector\_security\_groups](#input\_vpc\_connector\_security\_groups) | The security groups to use for the VPC Connector | `list(string)` | `[]` | no | 279 | | [vpc\_connector\_subnets](#input\_vpc\_connector\_subnets) | The subnets to use for the VPC Connector | `list(string)` | `[]` | no | 280 | 281 | ## Outputs 282 | 283 | | Name | Description | 284 | |------|-------------| 285 | | [access\_iam\_role\_arn](#output\_access\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 286 | | [access\_iam\_role\_name](#output\_access\_iam\_role\_name) | The name of the IAM role | 287 | | [access\_iam\_role\_unique\_id](#output\_access\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 288 | | [auto\_scaling\_configurations](#output\_auto\_scaling\_configurations) | Map of attribute maps for all autoscaling configurations created | 289 | | [connections](#output\_connections) | Map of attribute maps for all connections created | 290 | | [custom\_domain\_association\_certificate\_validation\_records](#output\_custom\_domain\_association\_certificate\_validation\_records) | A set of certificate CNAME records used for this domain name | 291 | | [custom\_domain\_association\_dns\_target](#output\_custom\_domain\_association\_dns\_target) | The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform | 292 | | [custom\_domain\_association\_id](#output\_custom\_domain\_association\_id) | The `domain_name` and `service_arn` separated by a comma (`,`) | 293 | | [instance\_iam\_role\_arn](#output\_instance\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 294 | | [instance\_iam\_role\_name](#output\_instance\_iam\_role\_name) | The name of the IAM role | 295 | | [instance\_iam\_role\_unique\_id](#output\_instance\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 296 | | [observability\_configuration\_arn](#output\_observability\_configuration\_arn) | ARN of this observability configuration | 297 | | [observability\_configuration\_latest](#output\_observability\_configuration\_latest) | Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name` | 298 | | [observability\_configuration\_revision](#output\_observability\_configuration\_revision) | The revision of the observability configuration | 299 | | [observability\_configuration\_status](#output\_observability\_configuration\_status) | The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion | 300 | | [service\_arn](#output\_service\_arn) | The Amazon Resource Name (ARN) of the service | 301 | | [service\_id](#output\_service\_id) | An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region | 302 | | [service\_status](#output\_service\_status) | The current state of the App Runner service | 303 | | [service\_url](#output\_service\_url) | A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application | 304 | | [vpc\_connector\_arn](#output\_vpc\_connector\_arn) | The Amazon Resource Name (ARN) of VPC connector | 305 | | [vpc\_connector\_revision](#output\_vpc\_connector\_revision) | The revision of VPC connector. It's unique among all the active connectors ("Status": "ACTIVE") that share the same Name | 306 | | [vpc\_connector\_status](#output\_vpc\_connector\_status) | The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted | 307 | | [vpc\_ingress\_connection\_arn](#output\_vpc\_ingress\_connection\_arn) | The Amazon Resource Name (ARN) of the VPC Ingress Connection | 308 | | [vpc\_ingress\_connection\_domain\_name](#output\_vpc\_ingress\_connection\_domain\_name) | The domain name associated with the VPC Ingress Connection resource | 309 | 310 | 311 | ## License 312 | 313 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-app-runner/blob/master/LICENSE). 314 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Please note - the examples provided serve two primary means: 4 | 5 | 1. Show users working examples of the various ways in which the module can be configured and features supported 6 | 2. A means of testing/validating module changes 7 | 8 | Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. 9 | -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # Complete AWS App Runner Example 2 | 3 | Configuration in this directory creates: 4 | 5 | - AppRunner shared configurations for: 6 | - GitHub connection 7 | - Autoscaling configuration for a "min" and "max" examples 8 | - A code based AppRunner service 9 | - Utilizes "min" autoscaling configuration created in shared configs 10 | - An image based AppRunner service 11 | - Utilizes "mega" autoscaling configuration created in shared configs 12 | - Creates a VPC connector to the associated VPC private subnets 13 | - Creates an example runtime environment variable and secret 14 | 15 | ## Usage 16 | 17 | To run this example you need to execute: 18 | 19 | 1. Fork [this repository](https://github.com/aws-containers/hello-app-runner) to the account/organization associated with the GitHub connection that will be created 20 | 21 | 2. 22 | ```bash 23 | $ terraform init 24 | $ terraform plan 25 | $ terraform apply -var="repository_url=" 26 | ``` 27 | 28 | 3. Once you have entered `yes` at the prompt, you will need to navigate to the AppRunner console -> GitHub connections -> Complete the handshake for the connection created by this example. 29 | 30 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 31 | 32 | 33 | ## Requirements 34 | 35 | | Name | Version | 36 | |------|---------| 37 | | [terraform](#requirement\_terraform) | >= 1.0 | 38 | | [aws](#requirement\_aws) | >= 4.51 | 39 | 40 | ## Providers 41 | 42 | | Name | Version | 43 | |------|---------| 44 | | [aws](#provider\_aws) | >= 4.51 | 45 | 46 | ## Modules 47 | 48 | | Name | Source | Version | 49 | |------|--------|---------| 50 | | [app\_runner\_code\_base](#module\_app\_runner\_code\_base) | ../.. | n/a | 51 | | [app\_runner\_disabled](#module\_app\_runner\_disabled) | ../.. | n/a | 52 | | [app\_runner\_image\_base](#module\_app\_runner\_image\_base) | ../.. | n/a | 53 | | [app\_runner\_private](#module\_app\_runner\_private) | ../.. | n/a | 54 | | [app\_runner\_shared\_configs](#module\_app\_runner\_shared\_configs) | ../.. | n/a | 55 | | [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 | 56 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | 57 | | [vpc\_endpoints](#module\_vpc\_endpoints) | terraform-aws-modules/vpc/aws//modules/vpc-endpoints | ~> 5.0 | 58 | | [vpc\_endpoints\_security\_group](#module\_vpc\_endpoints\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 | 59 | 60 | ## Resources 61 | 62 | | Name | Type | 63 | |------|------| 64 | | [aws_secretsmanager_secret.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | 65 | | [aws_secretsmanager_secret_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | 66 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 67 | 68 | ## Inputs 69 | 70 | | Name | Description | Type | Default | Required | 71 | |------|-------------|------|---------|:--------:| 72 | | [repository\_url](#input\_repository\_url) | Location of the repository that contains the source code. Repository must be located in same scope as the GitHub connection (user's profile or organization) | `string` | `"https://github.com/aws-containers/hello-app-runner"` | no | 73 | 74 | ## Outputs 75 | 76 | | Name | Description | 77 | |------|-------------| 78 | | [auto\_scaling\_configurations](#output\_auto\_scaling\_configurations) | Map of attribute maps for all autosclaing configurations created | 79 | | [code\_base\_access\_iam\_role\_arn](#output\_code\_base\_access\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 80 | | [code\_base\_access\_iam\_role\_name](#output\_code\_base\_access\_iam\_role\_name) | The name of the IAM role | 81 | | [code\_base\_access\_iam\_role\_unique\_id](#output\_code\_base\_access\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 82 | | [code\_base\_custom\_domain\_association\_certificate\_validation\_records](#output\_code\_base\_custom\_domain\_association\_certificate\_validation\_records) | A set of certificate CNAME records used for this domain name | 83 | | [code\_base\_custom\_domain\_association\_dns\_target](#output\_code\_base\_custom\_domain\_association\_dns\_target) | The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform | 84 | | [code\_base\_custom\_domain\_association\_id](#output\_code\_base\_custom\_domain\_association\_id) | The `domain_name` and `service_arn` separated by a comma (`,`) | 85 | | [code\_base\_instance\_iam\_role\_arn](#output\_code\_base\_instance\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 86 | | [code\_base\_instance\_iam\_role\_name](#output\_code\_base\_instance\_iam\_role\_name) | The name of the IAM role | 87 | | [code\_base\_instance\_iam\_role\_unique\_id](#output\_code\_base\_instance\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 88 | | [code\_base\_observability\_configuration\_arn](#output\_code\_base\_observability\_configuration\_arn) | ARN of this observability configuration | 89 | | [code\_base\_observability\_configuration\_latest](#output\_code\_base\_observability\_configuration\_latest) | Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name` | 90 | | [code\_base\_observability\_configuration\_revision](#output\_code\_base\_observability\_configuration\_revision) | The revision of the observability configuration | 91 | | [code\_base\_observability\_configuration\_status](#output\_code\_base\_observability\_configuration\_status) | The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion | 92 | | [code\_base\_service\_arn](#output\_code\_base\_service\_arn) | The Amazon Resource Name (ARN) of the service | 93 | | [code\_base\_service\_id](#output\_code\_base\_service\_id) | An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region | 94 | | [code\_base\_service\_status](#output\_code\_base\_service\_status) | The current state of the App Runner service | 95 | | [code\_base\_service\_url](#output\_code\_base\_service\_url) | A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application | 96 | | [code\_base\_vpc\_connector\_arn](#output\_code\_base\_vpc\_connector\_arn) | The Amazon Resource Name (ARN) of VPC connector | 97 | | [code\_base\_vpc\_connector\_revision](#output\_code\_base\_vpc\_connector\_revision) | The revision of VPC connector. It's unique among all the active connectors ("Status": "ACTIVE") that share the same Name | 98 | | [code\_base\_vpc\_connector\_status](#output\_code\_base\_vpc\_connector\_status) | The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted | 99 | | [code\_base\_vpc\_ingress\_connection\_arn](#output\_code\_base\_vpc\_ingress\_connection\_arn) | The Amazon Resource Name (ARN) of the VPC Ingress Connection | 100 | | [code\_base\_vpc\_ingress\_connection\_domain\_name](#output\_code\_base\_vpc\_ingress\_connection\_domain\_name) | The domain name associated with the VPC Ingress Connection resource | 101 | | [connections](#output\_connections) | Map of attribute maps for all connections created | 102 | | [image\_base\_access\_iam\_role\_arn](#output\_image\_base\_access\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 103 | | [image\_base\_access\_iam\_role\_name](#output\_image\_base\_access\_iam\_role\_name) | The name of the IAM role | 104 | | [image\_base\_access\_iam\_role\_unique\_id](#output\_image\_base\_access\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 105 | | [image\_base\_custom\_domain\_association\_certificate\_validation\_records](#output\_image\_base\_custom\_domain\_association\_certificate\_validation\_records) | A set of certificate CNAME records used for this domain name | 106 | | [image\_base\_custom\_domain\_association\_dns\_target](#output\_image\_base\_custom\_domain\_association\_dns\_target) | The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform | 107 | | [image\_base\_custom\_domain\_association\_id](#output\_image\_base\_custom\_domain\_association\_id) | The `domain_name` and `service_arn` separated by a comma (`,`) | 108 | | [image\_base\_instance\_iam\_role\_arn](#output\_image\_base\_instance\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 109 | | [image\_base\_instance\_iam\_role\_name](#output\_image\_base\_instance\_iam\_role\_name) | The name of the IAM role | 110 | | [image\_base\_instance\_iam\_role\_unique\_id](#output\_image\_base\_instance\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 111 | | [image\_base\_observability\_configuration\_arn](#output\_image\_base\_observability\_configuration\_arn) | ARN of this observability configuration | 112 | | [image\_base\_observability\_configuration\_latest](#output\_image\_base\_observability\_configuration\_latest) | Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name` | 113 | | [image\_base\_observability\_configuration\_revision](#output\_image\_base\_observability\_configuration\_revision) | The revision of the observability configuration | 114 | | [image\_base\_observability\_configuration\_status](#output\_image\_base\_observability\_configuration\_status) | The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion | 115 | | [image\_base\_service\_arn](#output\_image\_base\_service\_arn) | The Amazon Resource Name (ARN) of the service | 116 | | [image\_base\_service\_id](#output\_image\_base\_service\_id) | An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region | 117 | | [image\_base\_service\_status](#output\_image\_base\_service\_status) | The current state of the App Runner service | 118 | | [image\_base\_service\_url](#output\_image\_base\_service\_url) | A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application | 119 | | [image\_base\_vpc\_connector\_arn](#output\_image\_base\_vpc\_connector\_arn) | The Amazon Resource Name (ARN) of VPC connector | 120 | | [image\_base\_vpc\_connector\_revision](#output\_image\_base\_vpc\_connector\_revision) | The revision of VPC connector. It's unique among all the active connectors ("Status": "ACTIVE") that share the same Name | 121 | | [image\_base\_vpc\_connector\_status](#output\_image\_base\_vpc\_connector\_status) | The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted | 122 | | [image\_base\_vpc\_ingress\_connection\_arn](#output\_image\_base\_vpc\_ingress\_connection\_arn) | The Amazon Resource Name (ARN) of the VPC Ingress Connection | 123 | | [image\_base\_vpc\_ingress\_connection\_domain\_name](#output\_image\_base\_vpc\_ingress\_connection\_domain\_name) | The domain name associated with the VPC Ingress Connection resource | 124 | | [private\_access\_iam\_role\_arn](#output\_private\_access\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 125 | | [private\_access\_iam\_role\_name](#output\_private\_access\_iam\_role\_name) | The name of the IAM role | 126 | | [private\_access\_iam\_role\_unique\_id](#output\_private\_access\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 127 | | [private\_custom\_domain\_association\_certificate\_validation\_records](#output\_private\_custom\_domain\_association\_certificate\_validation\_records) | A set of certificate CNAME records used for this domain name | 128 | | [private\_custom\_domain\_association\_dns\_target](#output\_private\_custom\_domain\_association\_dns\_target) | The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform | 129 | | [private\_custom\_domain\_association\_id](#output\_private\_custom\_domain\_association\_id) | The `domain_name` and `service_arn` separated by a comma (`,`) | 130 | | [private\_instance\_iam\_role\_arn](#output\_private\_instance\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | 131 | | [private\_instance\_iam\_role\_name](#output\_private\_instance\_iam\_role\_name) | The name of the IAM role | 132 | | [private\_instance\_iam\_role\_unique\_id](#output\_private\_instance\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | 133 | | [private\_observability\_configuration\_arn](#output\_private\_observability\_configuration\_arn) | ARN of this observability configuration | 134 | | [private\_observability\_configuration\_latest](#output\_private\_observability\_configuration\_latest) | Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name` | 135 | | [private\_observability\_configuration\_revision](#output\_private\_observability\_configuration\_revision) | The revision of the observability configuration | 136 | | [private\_observability\_configuration\_status](#output\_private\_observability\_configuration\_status) | The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion | 137 | | [private\_service\_arn](#output\_private\_service\_arn) | The Amazon Resource Name (ARN) of the service | 138 | | [private\_service\_id](#output\_private\_service\_id) | An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region | 139 | | [private\_service\_status](#output\_private\_service\_status) | The current state of the App Runner service | 140 | | [private\_service\_url](#output\_private\_service\_url) | A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application | 141 | | [private\_vpc\_connector\_arn](#output\_private\_vpc\_connector\_arn) | The Amazon Resource Name (ARN) of VPC connector | 142 | | [private\_vpc\_connector\_revision](#output\_private\_vpc\_connector\_revision) | The revision of VPC connector. It's unique among all the active connectors ("Status": "ACTIVE") that share the same Name | 143 | | [private\_vpc\_connector\_status](#output\_private\_vpc\_connector\_status) | The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted | 144 | | [private\_vpc\_ingress\_connection\_arn](#output\_private\_vpc\_ingress\_connection\_arn) | The Amazon Resource Name (ARN) of the VPC Ingress Connection | 145 | | [private\_vpc\_ingress\_connection\_domain\_name](#output\_private\_vpc\_ingress\_connection\_domain\_name) | The domain name associated with the VPC Ingress Connection resource | 146 | 147 | 148 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-app-runner/blob/master/LICENSE). 149 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "us-east-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | tags = { 15 | Example = local.name 16 | GithubRepo = "terraform-aws-app-runner" 17 | GithubOrg = "terraform-aws-modules" 18 | } 19 | } 20 | 21 | ################################################################################ 22 | # App Runner Module 23 | ################################################################################ 24 | 25 | module "app_runner_shared_configs" { 26 | source = "../.." 27 | 28 | # Disable service resources 29 | create_service = false 30 | 31 | connections = { 32 | # The AWS Connector for GitHub connects to your GitHub account is a one-time setup, 33 | # You can reuse the connection for creating multiple App Runner services based on repositories in this account. 34 | # After creation, you must complete the authentication handshake using the App Runner console. 35 | github = { 36 | provider_type = "GITHUB" 37 | } 38 | } 39 | 40 | auto_scaling_configurations = { 41 | mini = { 42 | name = "mini" 43 | max_concurrency = 20 44 | max_size = 5 45 | min_size = 1 46 | 47 | tags = { 48 | Type = "Mini" 49 | } 50 | } 51 | 52 | mega = { 53 | name = "mega" 54 | max_concurrency = 200 55 | max_size = 25 56 | min_size = 5 57 | 58 | tags = { 59 | Type = "MEGA" 60 | } 61 | } 62 | } 63 | 64 | tags = local.tags 65 | } 66 | 67 | module "app_runner_code_base" { 68 | source = "../.." 69 | 70 | service_name = "${local.name}-code-base" 71 | 72 | # Pulling from shared configs 73 | auto_scaling_configuration_arn = module.app_runner_shared_configs.auto_scaling_configurations["mini"].arn 74 | 75 | source_configuration = { 76 | authentication_configuration = { 77 | # Pulling from shared configs 78 | connection_arn = module.app_runner_shared_configs.connections["github"].arn 79 | } 80 | auto_deployments_enabled = false 81 | code_repository = { 82 | code_configuration = { 83 | configuration_source = "REPOSITORY" 84 | } 85 | repository_url = var.repository_url 86 | source_code_version = { 87 | type = "BRANCH" 88 | value = "main" 89 | } 90 | } 91 | } 92 | 93 | tags = local.tags 94 | } 95 | 96 | module "app_runner_image_base" { 97 | source = "../.." 98 | 99 | service_name = "${local.name}-image-base" 100 | 101 | # Pulling from shared configs 102 | auto_scaling_configuration_arn = module.app_runner_shared_configs.auto_scaling_configurations["mega"].arn 103 | 104 | # IAM instance profile permissions to access secrets 105 | instance_policy_statements = { 106 | GetSecretValue = { 107 | actions = ["secretsmanager:GetSecretValue"] 108 | resources = [aws_secretsmanager_secret.this.arn] 109 | } 110 | } 111 | 112 | source_configuration = { 113 | auto_deployments_enabled = false 114 | image_repository = { 115 | image_configuration = { 116 | port = 8000 117 | runtime_environment_variables = { 118 | MY_VARIABLE = "hello!" 119 | } 120 | runtime_environment_secrets = { 121 | MY_SECRET = aws_secretsmanager_secret.this.arn 122 | } 123 | } 124 | image_identifier = "public.ecr.aws/aws-containers/hello-app-runner:latest" 125 | image_repository_type = "ECR_PUBLIC" 126 | } 127 | } 128 | 129 | # # Requires manual intervention to validate records 130 | # # https://github.com/hashicorp/terraform-provider-aws/issues/23460 131 | # create_custom_domain_association = true 132 | # hosted_zone_id = "" 133 | # domain_name = "" 134 | # enable_www_subdomain = true 135 | 136 | create_vpc_connector = true 137 | vpc_connector_subnets = module.vpc.private_subnets 138 | vpc_connector_security_groups = [module.security_group.security_group_id] 139 | network_configuration = { 140 | egress_configuration = { 141 | egress_type = "VPC" 142 | } 143 | } 144 | 145 | enable_observability_configuration = true 146 | 147 | tags = local.tags 148 | } 149 | 150 | module "app_runner_private" { 151 | source = "../.." 152 | 153 | service_name = "${local.name}-private" 154 | 155 | # Pulling from shared configs 156 | auto_scaling_configuration_arn = module.app_runner_shared_configs.auto_scaling_configurations["mega"].arn 157 | 158 | # IAM instance profile permissions to access secrets 159 | instance_policy_statements = { 160 | GetSecretValue = { 161 | actions = ["secretsmanager:GetSecretValue"] 162 | resources = [aws_secretsmanager_secret.this.arn] 163 | } 164 | } 165 | 166 | source_configuration = { 167 | auto_deployments_enabled = false 168 | image_repository = { 169 | image_configuration = { 170 | port = 8000 171 | runtime_environment_variables = { 172 | MY_VARIABLE = "hello!" 173 | } 174 | runtime_environment_secrets = { 175 | MY_SECRET = aws_secretsmanager_secret.this.arn 176 | } 177 | } 178 | image_identifier = "public.ecr.aws/aws-containers/hello-app-runner:latest" 179 | image_repository_type = "ECR_PUBLIC" 180 | } 181 | } 182 | 183 | create_ingress_vpc_connection = true 184 | ingress_vpc_id = module.vpc.vpc_id 185 | ingress_vpc_endpoint_id = module.vpc_endpoints.endpoints["apprunner"].id 186 | 187 | create_vpc_connector = true 188 | vpc_connector_subnets = module.vpc.private_subnets 189 | vpc_connector_security_groups = [module.security_group.security_group_id] 190 | 191 | network_configuration = { 192 | ingress_configuration = { 193 | is_publicly_accessible = false 194 | } 195 | egress_configuration = { 196 | egress_type = "VPC" 197 | } 198 | } 199 | 200 | enable_observability_configuration = true 201 | 202 | tags = local.tags 203 | } 204 | 205 | module "app_runner_disabled" { 206 | source = "../.." 207 | 208 | create = false 209 | } 210 | 211 | ################################################################################ 212 | # Supporting Resources 213 | ################################################################################ 214 | 215 | module "vpc" { 216 | source = "terraform-aws-modules/vpc/aws" 217 | version = "~> 5.0" 218 | 219 | name = local.name 220 | cidr = local.vpc_cidr 221 | 222 | azs = local.azs 223 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] 224 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] 225 | 226 | enable_nat_gateway = false 227 | 228 | tags = local.tags 229 | } 230 | 231 | module "vpc_endpoints" { 232 | source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" 233 | version = "~> 5.0" 234 | 235 | vpc_id = module.vpc.vpc_id 236 | security_group_ids = [module.vpc_endpoints_security_group.security_group_id] 237 | 238 | endpoints = { 239 | apprunner = { 240 | service = "apprunner.requests" 241 | # private_dns_enabled = true 242 | subnet_ids = module.vpc.private_subnets 243 | tags = { Name = "${local.name}-apprunner" } 244 | }, 245 | } 246 | 247 | tags = local.tags 248 | } 249 | 250 | module "security_group" { 251 | source = "terraform-aws-modules/security-group/aws" 252 | version = "~> 5.0" 253 | 254 | name = local.name 255 | description = "Security group for AppRunner connector" 256 | vpc_id = module.vpc.vpc_id 257 | 258 | egress_rules = ["http-80-tcp"] 259 | egress_cidr_blocks = module.vpc.private_subnets_cidr_blocks 260 | 261 | tags = local.tags 262 | } 263 | 264 | module "vpc_endpoints_security_group" { 265 | source = "terraform-aws-modules/security-group/aws" 266 | version = "~> 5.0" 267 | 268 | name = "${local.name}-vpc-endpoints" 269 | description = "Security group for VPC Endpoints" 270 | vpc_id = module.vpc.vpc_id 271 | 272 | egress_rules = ["https-443-tcp"] 273 | egress_cidr_blocks = [module.vpc.vpc_cidr_block] 274 | 275 | tags = local.tags 276 | } 277 | 278 | resource "aws_secretsmanager_secret" "this" { 279 | name_prefix = local.name 280 | recovery_window_in_days = 0 281 | } 282 | 283 | resource "aws_secretsmanager_secret_version" "this" { 284 | secret_id = aws_secretsmanager_secret.this.id 285 | secret_string = "example" 286 | } 287 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Shared Configurations 3 | ################################################################################ 4 | 5 | output "connections" { 6 | description = "Map of attribute maps for all connections created" 7 | value = module.app_runner_shared_configs.connections 8 | } 9 | 10 | output "auto_scaling_configurations" { 11 | description = "Map of attribute maps for all autosclaing configurations created" 12 | value = module.app_runner_shared_configs.auto_scaling_configurations 13 | } 14 | 15 | ################################################################################ 16 | # Code Base 17 | ################################################################################ 18 | 19 | output "code_base_service_arn" { 20 | description = "The Amazon Resource Name (ARN) of the service" 21 | value = module.app_runner_code_base.service_arn 22 | } 23 | 24 | output "code_base_service_id" { 25 | description = "An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region" 26 | value = module.app_runner_code_base.service_id 27 | } 28 | 29 | output "code_base_service_url" { 30 | description = "A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application" 31 | value = module.app_runner_code_base.service_url 32 | } 33 | 34 | output "code_base_service_status" { 35 | description = "The current state of the App Runner service" 36 | value = module.app_runner_code_base.service_status 37 | } 38 | 39 | output "code_base_access_iam_role_name" { 40 | description = "The name of the IAM role" 41 | value = module.app_runner_code_base.access_iam_role_name 42 | } 43 | 44 | output "code_base_access_iam_role_arn" { 45 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 46 | value = module.app_runner_code_base.access_iam_role_arn 47 | } 48 | 49 | output "code_base_access_iam_role_unique_id" { 50 | description = "Stable and unique string identifying the IAM role" 51 | value = module.app_runner_code_base.access_iam_role_unique_id 52 | } 53 | 54 | output "code_base_instance_iam_role_name" { 55 | description = "The name of the IAM role" 56 | value = module.app_runner_code_base.instance_iam_role_name 57 | } 58 | 59 | output "code_base_instance_iam_role_arn" { 60 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 61 | value = module.app_runner_code_base.instance_iam_role_arn 62 | } 63 | 64 | output "code_base_instance_iam_role_unique_id" { 65 | description = "Stable and unique string identifying the IAM role" 66 | value = module.app_runner_code_base.instance_iam_role_unique_id 67 | } 68 | 69 | output "code_base_vpc_ingress_connection_arn" { 70 | description = "The Amazon Resource Name (ARN) of the VPC Ingress Connection" 71 | value = module.app_runner_code_base.vpc_ingress_connection_arn 72 | } 73 | 74 | output "code_base_vpc_ingress_connection_domain_name" { 75 | description = "The domain name associated with the VPC Ingress Connection resource" 76 | value = module.app_runner_code_base.vpc_ingress_connection_domain_name 77 | } 78 | 79 | output "code_base_custom_domain_association_id" { 80 | description = "The `domain_name` and `service_arn` separated by a comma (`,`)" 81 | value = module.app_runner_code_base.custom_domain_association_id 82 | } 83 | 84 | output "code_base_custom_domain_association_certificate_validation_records" { 85 | description = "A set of certificate CNAME records used for this domain name" 86 | value = module.app_runner_code_base.custom_domain_association_certificate_validation_records 87 | } 88 | 89 | output "code_base_custom_domain_association_dns_target" { 90 | description = "The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform" 91 | value = module.app_runner_code_base.custom_domain_association_dns_target 92 | } 93 | 94 | output "code_base_vpc_connector_arn" { 95 | description = "The Amazon Resource Name (ARN) of VPC connector" 96 | value = module.app_runner_code_base.vpc_connector_arn 97 | } 98 | 99 | output "code_base_vpc_connector_status" { 100 | description = "The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted" 101 | value = module.app_runner_code_base.vpc_connector_status 102 | } 103 | 104 | output "code_base_vpc_connector_revision" { 105 | description = "The revision of VPC connector. It's unique among all the active connectors (\"Status\": \"ACTIVE\") that share the same Name" 106 | value = module.app_runner_code_base.vpc_connector_revision 107 | } 108 | 109 | output "code_base_observability_configuration_arn" { 110 | description = "ARN of this observability configuration" 111 | value = module.app_runner_code_base.observability_configuration_arn 112 | } 113 | 114 | output "code_base_observability_configuration_revision" { 115 | description = "The revision of the observability configuration" 116 | value = module.app_runner_code_base.observability_configuration_revision 117 | } 118 | 119 | output "code_base_observability_configuration_latest" { 120 | description = "Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name`" 121 | value = module.app_runner_code_base.observability_configuration_latest 122 | } 123 | 124 | output "code_base_observability_configuration_status" { 125 | description = "The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion" 126 | value = module.app_runner_code_base.observability_configuration_status 127 | } 128 | 129 | ################################################################################ 130 | # Image Base 131 | ################################################################################ 132 | 133 | output "image_base_service_arn" { 134 | description = "The Amazon Resource Name (ARN) of the service" 135 | value = module.app_runner_image_base.service_arn 136 | } 137 | 138 | output "image_base_service_id" { 139 | description = "An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region" 140 | value = module.app_runner_image_base.service_id 141 | } 142 | 143 | output "image_base_service_url" { 144 | description = "A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application" 145 | value = module.app_runner_image_base.service_url 146 | } 147 | 148 | output "image_base_service_status" { 149 | description = "The current state of the App Runner service" 150 | value = module.app_runner_image_base.service_status 151 | } 152 | 153 | output "image_base_access_iam_role_name" { 154 | description = "The name of the IAM role" 155 | value = module.app_runner_image_base.access_iam_role_name 156 | } 157 | 158 | output "image_base_access_iam_role_arn" { 159 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 160 | value = module.app_runner_image_base.access_iam_role_arn 161 | } 162 | 163 | output "image_base_access_iam_role_unique_id" { 164 | description = "Stable and unique string identifying the IAM role" 165 | value = module.app_runner_image_base.access_iam_role_unique_id 166 | } 167 | 168 | output "image_base_instance_iam_role_name" { 169 | description = "The name of the IAM role" 170 | value = module.app_runner_image_base.instance_iam_role_name 171 | } 172 | 173 | output "image_base_instance_iam_role_arn" { 174 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 175 | value = module.app_runner_image_base.instance_iam_role_arn 176 | } 177 | 178 | output "image_base_instance_iam_role_unique_id" { 179 | description = "Stable and unique string identifying the IAM role" 180 | value = module.app_runner_image_base.instance_iam_role_unique_id 181 | } 182 | 183 | output "image_base_vpc_ingress_connection_arn" { 184 | description = "The Amazon Resource Name (ARN) of the VPC Ingress Connection" 185 | value = module.app_runner_image_base.vpc_ingress_connection_arn 186 | } 187 | 188 | output "image_base_vpc_ingress_connection_domain_name" { 189 | description = "The domain name associated with the VPC Ingress Connection resource" 190 | value = module.app_runner_image_base.vpc_ingress_connection_domain_name 191 | } 192 | 193 | output "image_base_custom_domain_association_id" { 194 | description = "The `domain_name` and `service_arn` separated by a comma (`,`)" 195 | value = module.app_runner_image_base.custom_domain_association_id 196 | } 197 | 198 | output "image_base_custom_domain_association_certificate_validation_records" { 199 | description = "A set of certificate CNAME records used for this domain name" 200 | value = module.app_runner_image_base.custom_domain_association_certificate_validation_records 201 | } 202 | 203 | output "image_base_custom_domain_association_dns_target" { 204 | description = "The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform" 205 | value = module.app_runner_image_base.custom_domain_association_dns_target 206 | } 207 | 208 | output "image_base_vpc_connector_arn" { 209 | description = "The Amazon Resource Name (ARN) of VPC connector" 210 | value = module.app_runner_image_base.vpc_connector_arn 211 | } 212 | 213 | output "image_base_vpc_connector_status" { 214 | description = "The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted" 215 | value = module.app_runner_image_base.vpc_connector_status 216 | } 217 | 218 | output "image_base_vpc_connector_revision" { 219 | description = "The revision of VPC connector. It's unique among all the active connectors (\"Status\": \"ACTIVE\") that share the same Name" 220 | value = module.app_runner_image_base.vpc_connector_revision 221 | } 222 | 223 | output "image_base_observability_configuration_arn" { 224 | description = "ARN of this observability configuration" 225 | value = module.app_runner_image_base.observability_configuration_arn 226 | } 227 | 228 | output "image_base_observability_configuration_revision" { 229 | description = "The revision of the observability configuration" 230 | value = module.app_runner_image_base.observability_configuration_revision 231 | } 232 | 233 | output "image_base_observability_configuration_latest" { 234 | description = "Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name`" 235 | value = module.app_runner_image_base.observability_configuration_latest 236 | } 237 | 238 | output "image_base_observability_configuration_status" { 239 | description = "The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion" 240 | value = module.app_runner_image_base.observability_configuration_status 241 | } 242 | 243 | ################################################################################ 244 | # Private 245 | ################################################################################ 246 | 247 | output "private_service_arn" { 248 | description = "The Amazon Resource Name (ARN) of the service" 249 | value = module.app_runner_private.service_arn 250 | } 251 | 252 | output "private_service_id" { 253 | description = "An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region" 254 | value = module.app_runner_private.service_id 255 | } 256 | 257 | output "private_service_url" { 258 | description = "A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application" 259 | value = module.app_runner_private.service_url 260 | } 261 | 262 | output "private_service_status" { 263 | description = "The current state of the App Runner service" 264 | value = module.app_runner_private.service_status 265 | } 266 | 267 | output "private_access_iam_role_name" { 268 | description = "The name of the IAM role" 269 | value = module.app_runner_private.access_iam_role_name 270 | } 271 | 272 | output "private_access_iam_role_arn" { 273 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 274 | value = module.app_runner_private.access_iam_role_arn 275 | } 276 | 277 | output "private_access_iam_role_unique_id" { 278 | description = "Stable and unique string identifying the IAM role" 279 | value = module.app_runner_private.access_iam_role_unique_id 280 | } 281 | 282 | output "private_instance_iam_role_name" { 283 | description = "The name of the IAM role" 284 | value = module.app_runner_private.instance_iam_role_name 285 | } 286 | 287 | output "private_instance_iam_role_arn" { 288 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 289 | value = module.app_runner_private.instance_iam_role_arn 290 | } 291 | 292 | output "private_instance_iam_role_unique_id" { 293 | description = "Stable and unique string identifying the IAM role" 294 | value = module.app_runner_private.instance_iam_role_unique_id 295 | } 296 | 297 | output "private_vpc_ingress_connection_arn" { 298 | description = "The Amazon Resource Name (ARN) of the VPC Ingress Connection" 299 | value = module.app_runner_private.vpc_ingress_connection_arn 300 | } 301 | 302 | output "private_vpc_ingress_connection_domain_name" { 303 | description = "The domain name associated with the VPC Ingress Connection resource" 304 | value = module.app_runner_private.vpc_ingress_connection_domain_name 305 | } 306 | 307 | output "private_custom_domain_association_id" { 308 | description = "The `domain_name` and `service_arn` separated by a comma (`,`)" 309 | value = module.app_runner_private.custom_domain_association_id 310 | } 311 | 312 | output "private_custom_domain_association_certificate_validation_records" { 313 | description = "A set of certificate CNAME records used for this domain name" 314 | value = module.app_runner_private.custom_domain_association_certificate_validation_records 315 | } 316 | 317 | output "private_custom_domain_association_dns_target" { 318 | description = "The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform" 319 | value = module.app_runner_private.custom_domain_association_dns_target 320 | } 321 | 322 | output "private_vpc_connector_arn" { 323 | description = "The Amazon Resource Name (ARN) of VPC connector" 324 | value = module.app_runner_private.vpc_connector_arn 325 | } 326 | 327 | output "private_vpc_connector_status" { 328 | description = "The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted" 329 | value = module.app_runner_private.vpc_connector_status 330 | } 331 | 332 | output "private_vpc_connector_revision" { 333 | description = "The revision of VPC connector. It's unique among all the active connectors (\"Status\": \"ACTIVE\") that share the same Name" 334 | value = module.app_runner_private.vpc_connector_revision 335 | } 336 | 337 | output "private_observability_configuration_arn" { 338 | description = "ARN of this observability configuration" 339 | value = module.app_runner_private.observability_configuration_arn 340 | } 341 | 342 | output "private_observability_configuration_revision" { 343 | description = "The revision of the observability configuration" 344 | value = module.app_runner_private.observability_configuration_revision 345 | } 346 | 347 | output "private_observability_configuration_latest" { 348 | description = "Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name`" 349 | value = module.app_runner_private.observability_configuration_latest 350 | } 351 | 352 | output "private_observability_configuration_status" { 353 | description = "The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion" 354 | value = module.app_runner_private.observability_configuration_status 355 | } 356 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | variable "repository_url" { 2 | description = "Location of the repository that contains the source code. Repository must be located in same scope as the GitHub connection (user's profile or organization)" 3 | type = string 4 | default = "https://github.com/aws-containers/hello-app-runner" # clone to your account associated with the GitHub connection 5 | } 6 | -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.51" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "current" {} 2 | 3 | ################################################################################ 4 | # Service 5 | ################################################################################ 6 | 7 | locals { 8 | create_service = var.create && var.create_service 9 | 10 | # Ensure instance role created is attached even if no values are provided via `var.instance_configuration` 11 | instance_configuration = local.create_instance_iam_role ? merge( 12 | var.instance_configuration, 13 | { instance_role_arn = aws_iam_role.instance[0].arn } 14 | ) : var.instance_configuration 15 | 16 | # Ensure access role created is attached even if no values are provided via `var.source_configuration` 17 | source_configuration = local.create_access_iam_role ? merge( 18 | var.source_configuration, 19 | { authentication_configuration = { access_role_arn = aws_iam_role.access[0].arn } } 20 | ) : var.source_configuration 21 | 22 | # Ensure VPC connector created is attached even if no values are provided via `var.network_configuration` 23 | network_configuration = local.create_vpc_connector ? merge( 24 | var.network_configuration, 25 | { 26 | egress_configuration = { 27 | egress_type = "VPC" 28 | vpc_connector_arn = aws_apprunner_vpc_connector.this[0].arn 29 | } 30 | } 31 | ) : var.network_configuration 32 | } 33 | 34 | resource "aws_apprunner_service" "this" { 35 | count = local.create_service ? 1 : 0 36 | 37 | auto_scaling_configuration_arn = var.auto_scaling_configuration_arn 38 | 39 | dynamic "encryption_configuration" { 40 | for_each = length(var.encryption_configuration) > 0 ? [var.encryption_configuration] : [] 41 | 42 | content { 43 | kms_key = encryption_configuration.value.kms_key 44 | } 45 | } 46 | 47 | dynamic "health_check_configuration" { 48 | for_each = length(var.health_check_configuration) > 0 ? [var.health_check_configuration] : [] 49 | 50 | content { 51 | healthy_threshold = try(health_check_configuration.value.healthy_threshold, null) 52 | interval = try(health_check_configuration.value.interval, null) 53 | path = try(health_check_configuration.value.path, null) 54 | protocol = try(health_check_configuration.value.protocol, null) 55 | timeout = try(health_check_configuration.value.timeout, null) 56 | unhealthy_threshold = try(health_check_configuration.value.unhealthy_threshold, null) 57 | } 58 | } 59 | 60 | dynamic "instance_configuration" { 61 | for_each = length(local.instance_configuration) > 0 ? [local.instance_configuration] : [] 62 | 63 | content { 64 | cpu = try(instance_configuration.value.cpu, null) 65 | instance_role_arn = lookup(instance_configuration.value, "instance_role_arn", null) 66 | memory = try(instance_configuration.value.memory, null) 67 | } 68 | } 69 | 70 | dynamic "network_configuration" { 71 | for_each = length(local.network_configuration) > 0 ? [local.network_configuration] : [] 72 | 73 | content { 74 | dynamic "ingress_configuration" { 75 | for_each = try([network_configuration.value.ingress_configuration], []) 76 | 77 | content { 78 | is_publicly_accessible = try(ingress_configuration.value.is_publicly_accessible, null) 79 | } 80 | } 81 | 82 | dynamic "egress_configuration" { 83 | for_each = try([network_configuration.value.egress_configuration], []) 84 | 85 | content { 86 | egress_type = try(egress_configuration.value.egress_type, "VPC") 87 | vpc_connector_arn = try(egress_configuration.value.vpc_connector_arn, null) 88 | } 89 | } 90 | } 91 | } 92 | 93 | dynamic "observability_configuration" { 94 | for_each = local.enable_observability_configuration ? [1] : [] 95 | 96 | content { 97 | observability_configuration_arn = aws_apprunner_observability_configuration.this[0].arn 98 | observability_enabled = true 99 | } 100 | } 101 | 102 | service_name = var.service_name 103 | 104 | dynamic "source_configuration" { 105 | for_each = [local.source_configuration] 106 | 107 | content { 108 | dynamic "authentication_configuration" { 109 | for_each = try([source_configuration.value.authentication_configuration], []) 110 | 111 | content { 112 | access_role_arn = lookup(authentication_configuration.value, "access_role_arn", null) 113 | connection_arn = try(authentication_configuration.value.connection_arn, null) 114 | } 115 | } 116 | 117 | # Must be false when using public images or cross account images 118 | auto_deployments_enabled = try(source_configuration.value.auto_deployments_enabled, false) 119 | 120 | dynamic "code_repository" { 121 | for_each = try([source_configuration.value.code_repository], []) 122 | 123 | content { 124 | dynamic "code_configuration" { 125 | for_each = try([code_repository.value.code_configuration], []) 126 | 127 | content { 128 | dynamic "code_configuration_values" { 129 | for_each = try([code_configuration.value.code_configuration_values], []) 130 | 131 | content { 132 | build_command = try(code_configuration_values.value.build_command, null) 133 | port = try(code_configuration_values.value.port, null) 134 | runtime = code_configuration_values.value.runtime 135 | runtime_environment_variables = try(code_configuration_values.value.runtime_environment_variables, {}) 136 | runtime_environment_secrets = try(code_configuration_values.value.runtime_environment_secrets, {}) 137 | start_command = try(code_configuration_values.value.start_command, null) 138 | } 139 | } 140 | 141 | configuration_source = code_configuration.value.configuration_source 142 | } 143 | } 144 | 145 | repository_url = code_repository.value.repository_url 146 | 147 | dynamic "source_code_version" { 148 | for_each = [code_repository.value.source_code_version] 149 | 150 | content { 151 | type = try(source_code_version.value.type, "BRANCH") 152 | value = source_code_version.value.value 153 | } 154 | } 155 | } 156 | } 157 | 158 | dynamic "image_repository" { 159 | for_each = try([source_configuration.value.image_repository], []) 160 | 161 | content { 162 | dynamic "image_configuration" { 163 | for_each = try([image_repository.value.image_configuration], []) 164 | 165 | content { 166 | port = try(image_configuration.value.port, null) 167 | runtime_environment_variables = try(image_configuration.value.runtime_environment_variables, {}) 168 | runtime_environment_secrets = try(image_configuration.value.runtime_environment_secrets, {}) 169 | start_command = try(image_configuration.value.start_command, null) 170 | } 171 | } 172 | 173 | image_identifier = image_repository.value.image_identifier 174 | image_repository_type = image_repository.value.image_repository_type 175 | } 176 | } 177 | } 178 | } 179 | 180 | tags = var.tags 181 | } 182 | 183 | ################################################################################ 184 | # IAM Role - Access 185 | ################################################################################ 186 | 187 | locals { 188 | create_access_iam_role = local.create_service && var.create_access_iam_role 189 | access_iam_role_name = try(coalesce(var.access_iam_role_name, "${var.service_name}-access"), "") 190 | } 191 | 192 | data "aws_iam_policy_document" "access_assume_role" { 193 | count = local.create_access_iam_role ? 1 : 0 194 | 195 | statement { 196 | sid = "AccessAssumeRole" 197 | actions = ["sts:AssumeRole"] 198 | 199 | principals { 200 | type = "Service" 201 | identifiers = ["build.apprunner.${data.aws_partition.current.dns_suffix}"] 202 | } 203 | } 204 | } 205 | 206 | resource "aws_iam_role" "access" { 207 | count = local.create_access_iam_role ? 1 : 0 208 | 209 | name = var.access_iam_role_use_name_prefix ? null : local.access_iam_role_name 210 | name_prefix = var.access_iam_role_use_name_prefix ? "${local.access_iam_role_name}-" : null 211 | path = var.access_iam_role_path 212 | description = var.access_iam_role_description 213 | 214 | assume_role_policy = data.aws_iam_policy_document.access_assume_role[0].json 215 | permissions_boundary = var.access_iam_role_permissions_boundary 216 | force_detach_policies = true 217 | 218 | tags = var.tags 219 | } 220 | 221 | data "aws_iam_policy_document" "access" { 222 | count = local.create_access_iam_role && var.private_ecr_arn != null ? 1 : 0 223 | 224 | dynamic "statement" { 225 | for_each = var.private_ecr_arn != null ? [1] : [] 226 | 227 | content { 228 | sid = "ReadPrivateEcr" 229 | actions = [ 230 | "ecr:BatchGetImage", 231 | "ecr:DescribeImages", 232 | "ecr:GetDownloadUrlForLayer", 233 | ] 234 | resources = [var.private_ecr_arn] 235 | } 236 | } 237 | 238 | dynamic "statement" { 239 | for_each = var.private_ecr_arn != null ? [1] : [] 240 | 241 | content { 242 | sid = "AuthPrivateEcr" 243 | actions = [ 244 | "ecr:DescribeImages", 245 | "ecr:GetAuthorizationToken", 246 | ] 247 | resources = ["*"] 248 | } 249 | } 250 | } 251 | 252 | resource "aws_iam_policy" "access" { 253 | count = local.create_access_iam_role && var.private_ecr_arn != null ? 1 : 0 254 | 255 | name = var.access_iam_role_use_name_prefix ? null : local.access_iam_role_name 256 | name_prefix = var.access_iam_role_use_name_prefix ? "${local.access_iam_role_name}-" : null 257 | path = var.access_iam_role_path 258 | description = var.access_iam_role_description 259 | 260 | policy = data.aws_iam_policy_document.access[0].json 261 | } 262 | 263 | resource "aws_iam_role_policy_attachment" "access" { 264 | count = local.create_access_iam_role && var.private_ecr_arn != null ? 1 : 0 265 | 266 | policy_arn = aws_iam_policy.access[0].arn 267 | role = aws_iam_role.access[0].name 268 | } 269 | 270 | resource "aws_iam_role_policy_attachment" "access_additional" { 271 | for_each = { for k, v in var.access_iam_role_policies : k => v if local.create_access_iam_role } 272 | 273 | policy_arn = each.value 274 | role = aws_iam_role.access[0].name 275 | } 276 | 277 | ################################################################################ 278 | # IAM Role - Instance 279 | ################################################################################ 280 | 281 | locals { 282 | create_instance_iam_role = local.create_service && var.create_instance_iam_role 283 | instance_iam_role_name = try(coalesce(var.instance_iam_role_name, "${var.service_name}-instance"), "") 284 | } 285 | 286 | data "aws_iam_policy_document" "instance_assume_role" { 287 | count = var.create && var.create_instance_iam_role ? 1 : 0 288 | 289 | statement { 290 | sid = "InstanceAssumeRole" 291 | actions = ["sts:AssumeRole"] 292 | 293 | principals { 294 | type = "Service" 295 | identifiers = ["tasks.apprunner.${data.aws_partition.current.dns_suffix}"] 296 | } 297 | } 298 | } 299 | 300 | resource "aws_iam_role" "instance" { 301 | count = local.create_instance_iam_role ? 1 : 0 302 | 303 | name = var.instance_iam_role_use_name_prefix ? null : local.instance_iam_role_name 304 | name_prefix = var.instance_iam_role_use_name_prefix ? "${local.instance_iam_role_name}-" : null 305 | path = var.instance_iam_role_path 306 | description = var.instance_iam_role_description 307 | 308 | assume_role_policy = data.aws_iam_policy_document.instance_assume_role[0].json 309 | permissions_boundary = var.instance_iam_role_permissions_boundary 310 | force_detach_policies = true 311 | 312 | tags = var.tags 313 | } 314 | 315 | resource "aws_iam_role_policy_attachment" "instance_xray" { 316 | count = local.create_instance_iam_role && try(var.observability_configuration.value.observability_enabled, false) ? 1 : 0 317 | 318 | policy_arn = "arn:${data.aws_partition.current.id}:iam::aws:policy/AWSXRayDaemonWriteAccess" 319 | role = aws_iam_role.instance[0].name 320 | } 321 | 322 | resource "aws_iam_role_policy_attachment" "instance_additional" { 323 | for_each = { for k, v in var.instance_iam_role_policies : k => v if local.create_instance_iam_role } 324 | 325 | policy_arn = each.value 326 | role = aws_iam_role.instance[0].name 327 | } 328 | 329 | ################################################################################ 330 | # IAM Role Policy - Instance 331 | ################################################################################ 332 | 333 | locals { 334 | create_instance_role_policy = local.create_instance_iam_role && length(var.instance_policy_statements) > 0 335 | } 336 | 337 | data "aws_iam_policy_document" "instance" { 338 | count = local.create_instance_role_policy ? 1 : 0 339 | 340 | dynamic "statement" { 341 | for_each = var.instance_policy_statements 342 | 343 | content { 344 | sid = try(statement.value.sid, statement.key) 345 | actions = try(statement.value.actions, null) 346 | not_actions = try(statement.value.not_actions, null) 347 | effect = try(statement.value.effect, null) 348 | resources = try(statement.value.resources, null) 349 | not_resources = try(statement.value.not_resources, null) 350 | 351 | dynamic "principals" { 352 | for_each = try(statement.value.principals, []) 353 | 354 | content { 355 | type = principals.value.type 356 | identifiers = principals.value.identifiers 357 | } 358 | } 359 | 360 | dynamic "not_principals" { 361 | for_each = try(statement.value.not_principals, []) 362 | 363 | content { 364 | type = not_principals.value.type 365 | identifiers = not_principals.value.identifiers 366 | } 367 | } 368 | 369 | dynamic "condition" { 370 | for_each = try(statement.value.conditions, []) 371 | 372 | content { 373 | test = condition.value.test 374 | values = condition.value.values 375 | variable = condition.value.variable 376 | } 377 | } 378 | } 379 | } 380 | } 381 | 382 | resource "aws_iam_policy" "instance" { 383 | count = local.create_instance_role_policy ? 1 : 0 384 | 385 | name = var.instance_iam_role_use_name_prefix ? null : local.instance_iam_role_name 386 | name_prefix = var.instance_iam_role_use_name_prefix ? "${local.instance_iam_role_name}-" : null 387 | path = var.instance_iam_role_path 388 | description = var.instance_iam_role_description 389 | 390 | policy = data.aws_iam_policy_document.instance[0].json 391 | 392 | tags = var.tags 393 | } 394 | 395 | resource "aws_iam_role_policy_attachment" "instance" { 396 | count = local.create_instance_role_policy ? 1 : 0 397 | 398 | policy_arn = aws_iam_policy.instance[0].arn 399 | role = aws_iam_role.instance[0].name 400 | } 401 | 402 | ################################################################################ 403 | # VPC Ingress Configuration 404 | ################################################################################ 405 | 406 | resource "aws_apprunner_vpc_ingress_connection" "this" { 407 | count = local.create_service && var.create_ingress_vpc_connection ? 1 : 0 408 | 409 | name = var.service_name 410 | service_arn = aws_apprunner_service.this[0].arn 411 | 412 | ingress_vpc_configuration { 413 | vpc_id = var.ingress_vpc_id 414 | vpc_endpoint_id = var.ingress_vpc_endpoint_id 415 | } 416 | 417 | tags = var.tags 418 | } 419 | 420 | ################################################################################ 421 | # Custom Domain Association 422 | ################################################################################ 423 | 424 | locals { 425 | create_custom_domain_association = local.create_service && var.create_custom_domain_association 426 | } 427 | 428 | resource "aws_apprunner_custom_domain_association" "this" { 429 | count = local.create_custom_domain_association ? 1 : 0 430 | 431 | domain_name = var.domain_name 432 | enable_www_subdomain = var.enable_www_subdomain 433 | service_arn = aws_apprunner_service.this[0].arn 434 | } 435 | 436 | # # Requires manual intervention to validate records 437 | # # https://github.com/hashicorp/terraform-provider-aws/issues/23460 438 | # resource "aws_route53_record" "validation" { 439 | # count = length(aws_apprunner_custom_domain_association.this[0].certificate_validation_records) 440 | 441 | # allow_overwrite = true 442 | # name = aws_apprunner_custom_domain_association.this[0].certificate_validation_records.*.name[count.index] 443 | # records = [aws_apprunner_custom_domain_association.this[0].certificate_validation_records.*.value[count.index]] 444 | # ttl = 60 445 | # type = aws_apprunner_custom_domain_association.this[0].certificate_validation_records.*.type[count.index] 446 | # zone_id = var.hosted_zone_id 447 | # } 448 | 449 | # resource "aws_route53_record" "validation" { 450 | # for_each = { 451 | # for dvo in aws_apprunner_custom_domain_association.this[0].certificate_validation_records : dvo.name => { 452 | # name = dvo.name 453 | # record = dvo.value 454 | # type = dvo.type 455 | # } if local.create_custom_domain_association 456 | # } 457 | 458 | # allow_overwrite = true 459 | # name = each.value.name 460 | # records = [each.value.record] 461 | # ttl = 60 462 | # type = each.value.type 463 | # zone_id = var.hosted_zone_id 464 | # } 465 | 466 | # resource "aws_route53_record" "cname" { 467 | # count = local.create_custom_domain_association && var.domain_name_use_cname ? 1 : 0 468 | 469 | # allow_overwrite = true 470 | # name = var.domain_name 471 | # records = [aws_apprunner_custom_domain_association.this[0].dns_target] 472 | # ttl = 3600 473 | # type = "CNAME" 474 | # zone_id = var.hosted_zone_id 475 | # } 476 | 477 | # resource "aws_route53_record" "alias" { 478 | # for_each = { for k, v in toset(["A", "AAAA"]) : k => v if local.create_custom_domain_association && var.domain_name_use_cname } 479 | 480 | # zone_id = var.hosted_zone_id 481 | # name = var.domain_name 482 | # type = each.value 483 | 484 | # alias { 485 | # name = aws_apprunner_service.this[0].service_url 486 | # zone_id = ??? 487 | # evaluate_target_health = true 488 | # } 489 | # } 490 | 491 | ################################################################################ 492 | # VPC Connector 493 | ################################################################################ 494 | 495 | locals { 496 | create_vpc_connector = local.create_service && var.create_vpc_connector 497 | vpc_connector_name = try(coalesce(var.vpc_connector_name, var.service_name), "") 498 | } 499 | 500 | resource "aws_apprunner_vpc_connector" "this" { 501 | count = local.create_vpc_connector ? 1 : 0 502 | 503 | vpc_connector_name = local.vpc_connector_name 504 | subnets = var.vpc_connector_subnets 505 | security_groups = var.vpc_connector_security_groups 506 | 507 | tags = var.tags 508 | } 509 | 510 | ################################################################################ 511 | # Connection(s) 512 | ################################################################################ 513 | 514 | resource "aws_apprunner_connection" "this" { 515 | for_each = { for k, v in var.connections : k => v if var.create } 516 | 517 | connection_name = try(each.value.name, each.key) 518 | provider_type = try(each.value.provider_type, "GITHUB") 519 | 520 | tags = merge(var.tags, try(each.value.tags, {})) 521 | } 522 | 523 | ################################################################################ 524 | # Auto-Scaling Configuration(s) 525 | ################################################################################ 526 | 527 | resource "aws_apprunner_auto_scaling_configuration_version" "this" { 528 | for_each = { for k, v in var.auto_scaling_configurations : k => v if var.create } 529 | 530 | auto_scaling_configuration_name = try(each.value.name, each.key) 531 | max_concurrency = try(each.value.max_concurrency, null) 532 | max_size = try(each.value.max_size, null) 533 | min_size = try(each.value.min_size, null) 534 | 535 | tags = merge(var.tags, try(each.value.tags, {})) 536 | } 537 | 538 | ################################################################################ 539 | # Observability Configuration(s) 540 | ################################################################################ 541 | 542 | locals { 543 | enable_observability_configuration = local.create_service && var.enable_observability_configuration 544 | } 545 | 546 | resource "aws_apprunner_observability_configuration" "this" { 547 | count = local.enable_observability_configuration ? 1 : 0 548 | 549 | observability_configuration_name = var.service_name 550 | 551 | trace_configuration { 552 | vendor = "AWSXRAY" 553 | } 554 | 555 | tags = var.tags 556 | } 557 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Service 3 | ################################################################################ 4 | 5 | output "service_arn" { 6 | description = "The Amazon Resource Name (ARN) of the service" 7 | value = try(aws_apprunner_service.this[0].arn, null) 8 | } 9 | 10 | output "service_id" { 11 | description = "An alphanumeric ID that App Runner generated for this service. Unique within the AWS Region" 12 | value = try(aws_apprunner_service.this[0].service_id, null) 13 | } 14 | 15 | output "service_url" { 16 | description = "A subdomain URL that App Runner generated for this service. You can use this URL to access your service web application" 17 | value = try("https://${aws_apprunner_service.this[0].service_url}", null) 18 | } 19 | 20 | output "service_status" { 21 | description = "The current state of the App Runner service" 22 | value = try(aws_apprunner_service.this[0].status, null) 23 | } 24 | 25 | ################################################################################ 26 | # IAM Role - Access 27 | ################################################################################ 28 | 29 | output "access_iam_role_name" { 30 | description = "The name of the IAM role" 31 | value = try(aws_iam_role.access[0].name, null) 32 | } 33 | 34 | output "access_iam_role_arn" { 35 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 36 | value = try(aws_iam_role.access[0].arn, null) 37 | } 38 | 39 | output "access_iam_role_unique_id" { 40 | description = "Stable and unique string identifying the IAM role" 41 | value = try(aws_iam_role.access[0].unique_id, null) 42 | } 43 | 44 | ################################################################################ 45 | # IAM Role - Instance 46 | ################################################################################ 47 | 48 | output "instance_iam_role_name" { 49 | description = "The name of the IAM role" 50 | value = try(aws_iam_role.instance[0].name, null) 51 | } 52 | 53 | output "instance_iam_role_arn" { 54 | description = "The Amazon Resource Name (ARN) specifying the IAM role" 55 | value = try(aws_iam_role.instance[0].arn, null) 56 | } 57 | 58 | output "instance_iam_role_unique_id" { 59 | description = "Stable and unique string identifying the IAM role" 60 | value = try(aws_iam_role.instance[0].unique_id, null) 61 | } 62 | 63 | ################################################################################ 64 | # VPC Ingress Configuration 65 | ################################################################################ 66 | 67 | output "vpc_ingress_connection_arn" { 68 | description = "The Amazon Resource Name (ARN) of the VPC Ingress Connection" 69 | value = try(aws_apprunner_vpc_ingress_connection.this[0].arn, null) 70 | } 71 | 72 | output "vpc_ingress_connection_domain_name" { 73 | description = "The domain name associated with the VPC Ingress Connection resource" 74 | value = try(aws_apprunner_vpc_ingress_connection.this[0].domain_name, null) 75 | } 76 | 77 | ################################################################################ 78 | # Custom Domain Association 79 | ################################################################################ 80 | 81 | output "custom_domain_association_id" { 82 | description = "The `domain_name` and `service_arn` separated by a comma (`,`)" 83 | value = try(aws_apprunner_custom_domain_association.this[0].id, null) 84 | } 85 | 86 | output "custom_domain_association_certificate_validation_records" { 87 | description = "A set of certificate CNAME records used for this domain name" 88 | value = try(aws_apprunner_custom_domain_association.this[0].certificate_validation_records, null) 89 | } 90 | 91 | output "custom_domain_association_dns_target" { 92 | description = "The App Runner subdomain of the App Runner service. The custom domain name is mapped to this target name. Attribute only available if resource created (not imported) with Terraform" 93 | value = try(aws_apprunner_custom_domain_association.this[0].dns_target, null) 94 | } 95 | 96 | ################################################################################ 97 | # VPC Connector 98 | ################################################################################ 99 | 100 | output "vpc_connector_arn" { 101 | description = "The Amazon Resource Name (ARN) of VPC connector" 102 | value = try(aws_apprunner_vpc_connector.this[0].arn, null) 103 | } 104 | 105 | output "vpc_connector_status" { 106 | description = "The current state of the VPC connector. If the status of a connector revision is INACTIVE, it was deleted and can't be used. Inactive connector revisions are permanently removed some time after they are deleted" 107 | value = try(aws_apprunner_vpc_connector.this[0].status, null) 108 | } 109 | 110 | output "vpc_connector_revision" { 111 | description = "The revision of VPC connector. It's unique among all the active connectors (\"Status\": \"ACTIVE\") that share the same Name" 112 | value = try(aws_apprunner_vpc_connector.this[0].vpc_connector_revision, null) 113 | } 114 | 115 | ################################################################################ 116 | # Connection(s) 117 | ################################################################################ 118 | 119 | output "connections" { 120 | description = "Map of attribute maps for all connections created" 121 | value = aws_apprunner_connection.this 122 | } 123 | 124 | ################################################################################ 125 | # Auto-Scaling Configuration(s) 126 | ################################################################################ 127 | 128 | output "auto_scaling_configurations" { 129 | description = "Map of attribute maps for all autoscaling configurations created" 130 | value = aws_apprunner_auto_scaling_configuration_version.this 131 | } 132 | 133 | ################################################################################ 134 | # Observability Configuration 135 | ################################################################################ 136 | 137 | output "observability_configuration_arn" { 138 | description = "ARN of this observability configuration" 139 | value = try(aws_apprunner_observability_configuration.this[0].arn, null) 140 | } 141 | 142 | output "observability_configuration_revision" { 143 | description = "The revision of the observability configuration" 144 | value = try(aws_apprunner_observability_configuration.this[0].observability_configuration_revision, null) 145 | } 146 | 147 | output "observability_configuration_latest" { 148 | description = "Whether the observability configuration has the highest `observability_configuration_revision` among all configurations that share the same `observability_configuration_name`" 149 | value = try(aws_apprunner_observability_configuration.this[0].latest, null) 150 | } 151 | 152 | output "observability_configuration_status" { 153 | description = "The current state of the observability configuration. An `INACTIVE` configuration revision has been deleted and can't be used. It is permanently removed some time after deletion" 154 | value = try(aws_apprunner_observability_configuration.this[0].status, null) 155 | } 156 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "tags" { 8 | description = "A map of tags to add to all resources" 9 | type = map(string) 10 | default = {} 11 | } 12 | 13 | ################################################################################ 14 | # Service 15 | ################################################################################ 16 | 17 | variable "create_service" { 18 | description = "Determines whether the service will be created" 19 | type = bool 20 | default = true 21 | } 22 | 23 | variable "service_name" { 24 | description = "The name of the service" 25 | type = string 26 | default = "" 27 | } 28 | 29 | variable "auto_scaling_configuration_arn" { 30 | description = "ARN of an App Runner automatic scaling configuration resource that you want to associate with your service. If not provided, App Runner associates the latest revision of a default auto scaling configuration" 31 | type = string 32 | default = null 33 | } 34 | 35 | variable "encryption_configuration" { 36 | description = "The encryption configuration for the service" 37 | type = any 38 | default = {} 39 | } 40 | 41 | variable "health_check_configuration" { 42 | description = "The health check configuration for the service" 43 | type = any 44 | default = {} 45 | } 46 | 47 | variable "instance_configuration" { 48 | description = "The instance configuration for the service" 49 | type = any 50 | default = {} 51 | } 52 | 53 | variable "network_configuration" { 54 | description = "The network configuration for the service" 55 | type = any 56 | default = {} 57 | } 58 | 59 | variable "observability_configuration" { 60 | description = "The observability configuration for the service" 61 | type = any 62 | default = {} 63 | } 64 | 65 | variable "source_configuration" { 66 | description = "The source configuration for the service" 67 | type = any 68 | default = {} 69 | } 70 | 71 | ################################################################################ 72 | # IAM Role - Access 73 | ################################################################################ 74 | 75 | variable "create_access_iam_role" { 76 | description = "Determines whether an IAM role is created or to use an existing IAM role" 77 | type = bool 78 | default = false 79 | } 80 | 81 | variable "access_iam_role_name" { 82 | description = "Name to use on IAM role created" 83 | type = string 84 | default = null 85 | } 86 | 87 | variable "access_iam_role_use_name_prefix" { 88 | description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix" 89 | type = bool 90 | default = true 91 | } 92 | 93 | variable "access_iam_role_path" { 94 | description = "IAM role path" 95 | type = string 96 | default = null 97 | } 98 | 99 | variable "access_iam_role_description" { 100 | description = "Description of the role" 101 | type = string 102 | default = null 103 | } 104 | 105 | variable "access_iam_role_permissions_boundary" { 106 | description = "ARN of the policy that is used to set the permissions boundary for the IAM role" 107 | type = string 108 | default = null 109 | } 110 | 111 | variable "private_ecr_arn" { 112 | description = "The ARN of the private ECR repository that contains the service image to launch" 113 | type = string 114 | default = null 115 | } 116 | 117 | variable "access_iam_role_policies" { 118 | description = "IAM policies to attach to the IAM role" 119 | type = map(string) 120 | default = {} 121 | } 122 | 123 | ################################################################################ 124 | # IAM Role - Instance 125 | ################################################################################ 126 | 127 | variable "create_instance_iam_role" { 128 | description = "Determines whether an IAM role is created or to use an existing IAM role" 129 | type = bool 130 | default = true 131 | } 132 | 133 | variable "instance_iam_role_name" { 134 | description = "Name to use on IAM role created" 135 | type = string 136 | default = null 137 | } 138 | 139 | variable "instance_iam_role_use_name_prefix" { 140 | description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix" 141 | type = bool 142 | default = true 143 | } 144 | 145 | variable "instance_iam_role_path" { 146 | description = "IAM role path" 147 | type = string 148 | default = null 149 | } 150 | 151 | variable "instance_iam_role_description" { 152 | description = "Description of the role" 153 | type = string 154 | default = null 155 | } 156 | 157 | variable "instance_iam_role_permissions_boundary" { 158 | description = "ARN of the policy that is used to set the permissions boundary for the IAM role" 159 | type = string 160 | default = null 161 | } 162 | 163 | variable "instance_iam_role_policies" { 164 | description = "IAM policies to attach to the IAM role" 165 | type = map(string) 166 | default = {} 167 | } 168 | 169 | ################################################################################ 170 | # IAM Role Policy - Instance 171 | ################################################################################ 172 | 173 | variable "instance_policy_statements" { 174 | description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" 175 | type = any 176 | default = {} 177 | } 178 | 179 | ################################################################################ 180 | # VPC Ingress Configuration 181 | ################################################################################ 182 | 183 | variable "create_ingress_vpc_connection" { 184 | description = "Determines whether a VPC ingress configuration will be created" 185 | type = bool 186 | default = false 187 | } 188 | 189 | variable "ingress_vpc_id" { 190 | description = "The ID of the VPC that is used for the VPC ingress configuration" 191 | type = string 192 | default = "" 193 | } 194 | 195 | variable "ingress_vpc_endpoint_id" { 196 | description = "The ID of the VPC endpoint that is used for the VPC ingress configuration" 197 | type = string 198 | default = "" 199 | } 200 | 201 | ################################################################################ 202 | # Custom Domain Association 203 | ################################################################################ 204 | 205 | variable "create_custom_domain_association" { 206 | description = "Determines whether a Custom Domain Association will be created" 207 | type = bool 208 | default = false 209 | } 210 | 211 | variable "domain_name" { 212 | description = "The custom domain endpoint to association. Specify a base domain e.g., `example.com` or a subdomain e.g., `subdomain.example.com`" 213 | type = string 214 | default = "" 215 | } 216 | 217 | variable "enable_www_subdomain" { 218 | description = "Whether to associate the subdomain with the App Runner service in addition to the base domain. Defaults to `true`" 219 | type = bool 220 | default = null 221 | } 222 | 223 | # variable "hosted_zone_id" { 224 | # description = "The ID of the Route53 hosted zone that contains the domain for the `domain_name`" 225 | # type = string 226 | # default = "" 227 | # } 228 | 229 | ################################################################################ 230 | # VPC Connector 231 | ################################################################################ 232 | 233 | variable "create_vpc_connector" { 234 | description = "Determines whether a VPC Connector will be created" 235 | type = bool 236 | default = false 237 | } 238 | 239 | variable "vpc_connector_name" { 240 | description = "The name of the VPC Connector" 241 | type = string 242 | default = "" 243 | } 244 | 245 | variable "vpc_connector_subnets" { 246 | description = "The subnets to use for the VPC Connector" 247 | type = list(string) 248 | default = [] 249 | } 250 | 251 | variable "vpc_connector_security_groups" { 252 | description = "The security groups to use for the VPC Connector" 253 | type = list(string) 254 | default = [] 255 | } 256 | 257 | ################################################################################ 258 | # Connection(s) 259 | ################################################################################ 260 | 261 | variable "connections" { 262 | description = "Map of connection definitions to create" 263 | type = any 264 | default = {} 265 | } 266 | 267 | ################################################################################ 268 | # Autoscaling Configuration(s) 269 | ################################################################################ 270 | 271 | variable "auto_scaling_configurations" { 272 | description = "Map of auto-scaling configuration definitions to create" 273 | type = any 274 | default = {} 275 | } 276 | 277 | ################################################################################ 278 | # Observability Configuration 279 | ################################################################################ 280 | 281 | variable "enable_observability_configuration" { 282 | description = "Determines whether an X-Ray Observability Configuration will be created and assigned to the service" 283 | type = bool 284 | default = true 285 | } 286 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.51" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------