diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..88cb2519
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,30 @@
+# EditorConfig is awesome: http://EditorConfig.org
+# Uses editorconfig to maintain consistent coding styles
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+max_line_length = 80
+trim_trailing_whitespace = true
+
+[*.{tf,tfvars}]
+indent_size = 2
+indent_style = space
+
+[*.md]
+max_line_length = 0
+trim_trailing_whitespace = false
+
+[Makefile]
+tab_width = 2
+indent_style = tab
+
+[COMMIT_EDITMSG]
+max_line_length = 0
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
new file mode 100644
index 00000000..d887a660
--- /dev/null
+++ b/.github/workflows/lock.yml
@@ -0,0 +1,21 @@
+name: 'Lock Threads'
+
+on:
+ schedule:
+ - cron: '50 1 * * *'
+
+jobs:
+ lock:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v3
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-comment: >
+ I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
+ If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
+ issue-inactive-days: '30'
+ pr-comment: >
+ I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
+ If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
+ pr-inactive-days: '30'
diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml
new file mode 100644
index 00000000..cb32a0f8
--- /dev/null
+++ b/.github/workflows/pr-title.yml
@@ -0,0 +1,52 @@
+name: 'Validate PR title'
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - edited
+ - synchronize
+
+jobs:
+ main:
+ name: Validate PR title
+ runs-on: ubuntu-latest
+ steps:
+ # Please look up the latest version from
+ # https://github.com/amannn/action-semantic-pull-request/releases
+ - uses: amannn/action-semantic-pull-request@v5.0.2
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ # Configure which types are allowed.
+ # Default: https://github.com/commitizen/conventional-commit-types
+ types: |
+ fix
+ feat
+ docs
+ ci
+ chore
+ # Configure that a scope must always be provided.
+ requireScope: false
+ # Configure additional validation for the subject based on a regex.
+ # This example ensures the subject starts with an uppercase character.
+ subjectPattern: ^[A-Z].+$
+ # If `subjectPattern` is configured, you can use this property to override
+ # the default error message that is shown when the pattern doesn't match.
+ # The variables `subject` and `title` can be used within the message.
+ subjectPatternError: |
+ The subject "{subject}" found in the pull request title "{title}"
+ didn't match the configured pattern. Please ensure that the subject
+ starts with an uppercase character.
+ # For work-in-progress PRs you can typically use draft pull requests
+ # from Github. However, private repositories on the free plan don't have
+ # this option and therefore this action allows you to opt-in to using the
+ # special "[WIP]" prefix to indicate this state. This will avoid the
+ # validation of the PR title and the pull request checks remain pending.
+ # Note that a second check will be reported if this is enabled.
+ wip: true
+ # When using "Squash and merge" on a PR with only one commit, GitHub
+ # will suggest using that commit message instead of the PR title for the
+ # merge commit, and it's easy to commit this by mistake. Enable this option
+ # to also validate the commit message for one commit PRs.
+ validateSingleCommit: false
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 00000000..c00d2e83
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,86 @@
+name: Pre-Commit
+
+on:
+ pull_request:
+ branches:
+ - main
+ - master
+
+env:
+ TERRAFORM_DOCS_VERSION: v0.16.0
+
+jobs:
+ collectInputs:
+ name: Collect workflow inputs
+ runs-on: ubuntu-latest
+ outputs:
+ directories: ${{ steps.dirs.outputs.directories }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Get root directories
+ id: dirs
+ uses: clowdhaus/terraform-composite-actions/directories@v1.8.0
+
+ preCommitMinVersions:
+ name: Min TF pre-commit
+ needs: collectInputs
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ directory: ${{ fromJson(needs.collectInputs.outputs.directories) }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Terraform min/max versions
+ id: minMax
+ uses: clowdhaus/terraform-min-max@v1.2.0
+ with:
+ directory: ${{ matrix.directory }}
+
+ - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
+ # Run only validate pre-commit check on min version supported
+ if: ${{ matrix.directory != '.' }}
+ uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.0
+ with:
+ terraform-version: ${{ steps.minMax.outputs.minVersion }}
+ args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*'
+
+ - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
+ # Run only validate pre-commit check on min version supported
+ if: ${{ matrix.directory == '.' }}
+ uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.0
+ with:
+ terraform-version: ${{ steps.minMax.outputs.minVersion }}
+ args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)'
+
+ preCommitMaxVersion:
+ name: Max TF pre-commit
+ runs-on: ubuntu-latest
+ needs: collectInputs
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{github.event.pull_request.head.repo.full_name}}
+
+ - name: Terraform min/max versions
+ id: minMax
+ uses: clowdhaus/terraform-min-max@v1.2.0
+
+ - name: Install hcledit (for terraform_wrapper_module_for_each hook)
+ shell: bash
+ run: |
+ curl -L "$(curl -s https://api.github.com/repos/minamijoyo/hcledit/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > hcledit.tgz
+ sudo tar -xzf hcledit.tgz -C /usr/bin/ hcledit
+ rm -f hcledit.tgz 2> /dev/null
+ hcledit version
+
+ - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }}
+ uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.0
+ with:
+ terraform-version: ${{ steps.minMax.outputs.maxVersion }}
+ terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..98c8b258
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,37 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ - master
+ paths:
+ - '**/*.tpl'
+ - '**/*.py'
+ - '**/*.tf'
+ - '.github/workflows/release.yml'
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ # Skip running release workflow on forks
+ if: github.repository_owner == 'terraform-aws-modules'
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ persist-credentials: false
+ fetch-depth: 0
+
+ - name: Release
+ uses: cycjimmy/semantic-release-action@v2
+ with:
+ semantic_version: 18.0.0
+ extra_plugins: |
+ @semantic-release/changelog@6.0.0
+ @semantic-release/git@10.0.0
+ conventional-changelog-conventionalcommits@4.6.3
+ env:
+ GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml
new file mode 100644
index 00000000..50379957
--- /dev/null
+++ b/.github/workflows/stale-actions.yaml
@@ -0,0 +1,32 @@
+name: 'Mark or close stale issues and PRs'
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v6
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ # Staling issues and PR's
+ days-before-stale: 30
+ stale-issue-label: stale
+ stale-pr-label: stale
+ stale-issue-message: |
+ This issue has been automatically marked as stale because it has been open 30 days
+ with no activity. Remove stale label or comment or this issue will be closed in 10 days
+ stale-pr-message: |
+ This PR has been automatically marked as stale because it has been open 30 days
+ with no activity. Remove stale label or comment or this PR will be closed in 10 days
+ # Not stale if have this labels or part of milestone
+ exempt-issue-labels: bug,wip,on-hold
+ exempt-pr-labels: bug,wip,on-hold
+ exempt-all-milestones: true
+ # Close issue operations
+ # Label will be automatically removed if the issues are no longer closed nor locked.
+ days-before-close: 10
+ delete-branch: true
+ close-issue-message: This issue was automatically closed because of stale in 10 days
+ close-pr-message: This PR was automatically closed because of stale in 10 days
diff --git a/.gitignore b/.gitignore
index 3bfaa70f..397af322 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,29 @@
-.idea/*
-**/target/*
-*.iml
+# Local .terraform directories
+**/.terraform/*
+
+# Terraform lockfile
+.terraform.lock.hcl
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+
+# Exclude all .tfvars files, which are likely to contain sentitive data, such as
+# password, private keys, and other secrets. These should not be part of version
+# control as they are data points which are potentially sensitive and subject
+# to change depending on the environment.
+*.tfvars
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..314c02b1
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,30 @@
+repos:
+ - repo: https://github.com/antonbabenko/pre-commit-terraform
+ rev: v1.76.0
+ hooks:
+ - id: terraform_fmt
+ - id: terraform_wrapper_module_for_each
+ - id: terraform_validate
+ - id: terraform_docs
+ args:
+ - '--args=--lockfile=false'
+ - id: terraform_tflint
+ args:
+ - '--args=--only=terraform_deprecated_interpolation'
+ - '--args=--only=terraform_deprecated_index'
+ - '--args=--only=terraform_unused_declarations'
+ - '--args=--only=terraform_comment_syntax'
+ - '--args=--only=terraform_documented_outputs'
+ - '--args=--only=terraform_documented_variables'
+ - '--args=--only=terraform_typed_variables'
+ - '--args=--only=terraform_module_pinned_source'
+ - '--args=--only=terraform_naming_convention'
+ - '--args=--only=terraform_required_version'
+ - '--args=--only=terraform_required_providers'
+ - '--args=--only=terraform_standard_module_structure'
+ - '--args=--only=terraform_workspace_remote'
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.3.0
+ hooks:
+ - id: check-merge-conflict
+ - id: end-of-file-fixer
diff --git a/.releaserc.json b/.releaserc.json
new file mode 100644
index 00000000..66b3eefd
--- /dev/null
+++ b/.releaserc.json
@@ -0,0 +1,45 @@
+{
+ "branches": [
+ "main",
+ "master"
+ ],
+ "ci": false,
+ "plugins": [
+ [
+ "@semantic-release/commit-analyzer",
+ {
+ "preset": "conventionalcommits"
+ }
+ ],
+ [
+ "@semantic-release/release-notes-generator",
+ {
+ "preset": "conventionalcommits"
+ }
+ ],
+ [
+ "@semantic-release/github",
+ {
+ "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:",
+ "labels": false,
+ "releasedLabels": false
+ }
+ ],
+ [
+ "@semantic-release/changelog",
+ {
+ "changelogFile": "CHANGELOG.md",
+ "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file."
+ }
+ ],
+ [
+ "@semantic-release/git",
+ {
+ "assets": [
+ "CHANGELOG.md"
+ ],
+ "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
+ }
+ ]
+ ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..9e2fd19d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,533 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+### [4.2.1](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.2.0...v4.2.1) (2022-11-07)
+
+
+### Bug Fixes
+
+* Update CI configuration files to use latest version ([#303](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/303)) ([2151031](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/21510318bffcfa84a13c5ec8cbb93dff9871a4f9))
+
+## [4.2.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.1.4...v4.2.0) (2022-11-04)
+
+
+### Features
+
+* Add support for creating IAM role/instance profile with policies ([#302](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/302)) ([787132e](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/787132e5dbe7b58e4b9a62e1a69a682bcbb9bd58))
+
+### [4.1.4](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.1.3...v4.1.4) (2022-08-13)
+
+
+### Bug Fixes
+
+* Correct capacity reservation target ([#288](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/288)) ([135145e](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/135145e252c69814c019da49c638973f93523f6a))
+
+### [4.1.3](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.1.2...v4.1.3) (2022-08-12)
+
+
+### Bug Fixes
+
+* The capacity_reservation_specification default value is updated from null to {} ([#285](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/285)) ([9af6601](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/9af6601abbcfe06fc907ea1eb3abffe30d26daf2))
+
+### [4.1.2](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.1.1...v4.1.2) (2022-08-10)
+
+
+### Bug Fixes
+
+* Assignment of the Capacity Reservation id to an instance ([#282](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/282)) ([7f0a0ae](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/7f0a0ae66cbe50d0ea1c09191de4e82cfa8c4ca2))
+
+### [4.1.1](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.1.0...v4.1.1) (2022-07-21)
+
+
+### Bug Fixes
+
+* Creation of an Instance with a Capacity Reservation ID ([#278](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/278)) ([f12ac95](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/f12ac95aa309fdbf532ba1d5a9841690ca7fdb8e))
+
+## [4.1.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v4.0.0...v4.1.0) (2022-07-19)
+
+
+### Features
+
+* Add support for `disable_api_stop` attribute ([#275](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/275)) ([cb367ec](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/cb367ec54e4386512e37b8ef0b8d01c78f589fb1))
+
+## [4.0.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.6.0...v4.0.0) (2022-05-09)
+
+
+### ⚠ BREAKING CHANGES
+
+* Add support for user_data_replace_on_change, and updated AWS provider to v4.7+ (#272)
+
+### Features
+
+* Add support for user_data_replace_on_change, and updated AWS provider to v4.7+ ([#272](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/272)) ([4d7f3d8](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/4d7f3d873b0d2be7361d439e62b872a895073342))
+
+## [3.6.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.5.0...v3.6.0) (2022-05-06)
+
+
+### Features
+
+* Added wrappers automatically generated via pre-commit hook ([#271](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/271)) ([6e8c541](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/6e8c541b2d9b3fe54c9acc7f4d271648c5844c9b))
+
+## [3.5.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.4.0...v3.5.0) (2022-03-12)
+
+
+### Features
+
+* Made it clear that we stand with Ukraine ([6867788](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/6867788411a202b61187f9935e9eaa72a18f0bbe))
+
+## [3.4.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.3.0...v3.4.0) (2022-01-14)
+
+
+### Features
+
+* Add `instance_metadata_tags` attribute and bump AWS provider to support ([#254](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/254)) ([b2fb960](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/b2fb9604b32aa23d14a8a6e3cff761d6c69661b7))
+
+# [3.3.0](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.2.1...v3.3.0) (2021-11-22)
+
+
+### Features
+
+* Add instance IPv6 addresses to the outputs ([#249](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/249)) ([08bdf6a](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/08bdf6af910f665149d8d64a19175b89a8fac447))
+
+## [3.2.1](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.2.0...v3.2.1) (2021-11-22)
+
+
+### Bug Fixes
+
+* update CI/CD process to enable auto-release workflow ([#250](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/250)) ([1508c9e](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/commit/1508c9ec45ba954828c734326366143a17434a0f))
+
+
+## [v3.2.0] - 2021-10-07
+
+- feat: Add instance private IP to the outputs ([#241](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/241))
+
+
+
+## [v3.1.0] - 2021-08-27
+
+- feat: add support for spot instances via spot instance requests ([#236](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/236))
+- chore: update `README.md` example for making an encrypted AMI ([#235](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/235))
+
+
+
+## [v3.0.0] - 2021-08-26
+
+- BREAKING CHANGE: update module to include latest features and remove use of `count` for module `count`/`for_each` ([#234](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/234))
+
+
+
+## [v2.21.0] - 2021-08-26
+
+- feat: Add support for EBS GP3 throughput ([#233](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/233))
+
+
+
+## [v2.20.0] - 2021-08-25
+
+- feat: Add cpu optimization options ([#181](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/181))
+
+
+
+## [v2.19.0] - 2021-05-12
+
+- fix: root_block_device tags conflicts ([#220](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/220))
+
+
+
+## [v2.18.0] - 2021-05-11
+
+- feat: add tags support to root block device ([#218](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/218))
+- chore: update CI/CD to use stable `terraform-docs` release artifact and discoverable Apache2.0 license ([#217](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/217))
+- chore: Updated versions&comments in examples
+- chore: update documentation and pin `terraform_docs` version to avoid future changes ([#212](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/212))
+- chore: align ci-cd static checks to use individual minimum Terraform versions ([#207](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/207))
+- chore: add ci-cd workflow for pre-commit checks ([#201](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/201))
+
+
+
+## [v2.17.0] - 2021-02-20
+
+- chore: update documentation based on latest `terraform-docs` which includes module and resource sections ([#200](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/200))
+
+
+
+## [v2.16.0] - 2020-12-10
+
+- feat: Add support for metadata_options argument ([#193](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/193))
+
+
+
+## [v2.15.0] - 2020-06-10
+
+- feat: Add "num_suffix_format" variable for instance naming ([#147](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/147))
+
+
+
+## [v2.14.0] - 2020-06-10
+
+- Updated README
+- Updated t instance type check to include t3a type ([#145](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/145))
+- Updated README
+- Instance count as output ([#121](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/121))
+- Added user_data_base64 (fixed [#117](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/117)) ([#137](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/137))
+- Added support for network_interface and arn ([#136](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/136))
+- Update outputs to remove unneeded function wrappers ([#135](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/135))
+- Track all changes (remove ignore_changes lifecycle) ([#125](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/125))
+- Add encrypted and kms_key_id arguments to the ebs_* and root_* block ([#124](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/124))
+- Remove T2 specifics to unify Terraform object names inside TF State ([#111](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/111))
+- Fixed output of placement_group (fixed [#104](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/104)) ([#110](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/110))
+- Add get_password_data ([#105](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/105))
+- Fixed when private_ips is empty (fixed [#103](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/103))
+- Added support for the list of private_ips (fixes [#102](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/102)) ([#103](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/103))
+- Added support for placement group and volume tags ([#96](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/96))
+- Terraform 0.12 update ([#93](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/93))
+
+
+
+## [v1.25.0] - 2020-03-05
+
+- Updated t instance type check to include t3a type ([#146](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/146))
+- Added placement groups ([#94](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/94))
+- Revert example
+- Added placement groups
+- Add volume tags naming and output ([#82](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/82))
+
+
+
+## [v2.13.0] - 2020-03-05
+
+- Updated t instance type check to include t3a type ([#145](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/145))
+
+
+
+## [v2.12.0] - 2019-11-19
+
+- Updated README
+- Instance count as output ([#121](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/121))
+
+
+
+## [v2.11.0] - 2019-11-19
+
+- Added user_data_base64 (fixed [#117](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/117)) ([#137](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/137))
+
+
+
+## [v2.10.0] - 2019-11-19
+
+- Added support for network_interface and arn ([#136](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/136))
+
+
+
+## [v2.9.0] - 2019-11-19
+
+- Update outputs to remove unneeded function wrappers ([#135](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/135))
+
+
+
+## [v2.8.0] - 2019-08-27
+
+- Track all changes (remove ignore_changes lifecycle) ([#125](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/125))
+
+
+
+## [v2.7.0] - 2019-08-27
+
+- Add encrypted and kms_key_id arguments to the ebs_* and root_* block ([#124](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/124))
+
+
+
+## [v2.6.0] - 2019-07-21
+
+- Remove T2 specifics to unify Terraform object names inside TF State ([#111](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/111))
+
+
+
+## [v2.5.0] - 2019-07-08
+
+- Fixed output of placement_group (fixed [#104](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/104)) ([#110](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/110))
+
+
+
+## [v2.4.0] - 2019-06-24
+
+- Add get_password_data ([#105](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/105))
+
+
+
+## [v2.3.0] - 2019-06-15
+
+- Fixed when private_ips is empty (fixed [#103](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/103))
+
+
+
+## [v2.2.0] - 2019-06-14
+
+- Added support for the list of private_ips (fixes [#102](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/102)) ([#103](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/103))
+
+
+
+## [v2.1.0] - 2019-06-08
+
+- Added support for placement group and volume tags ([#96](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/96))
+- Terraform 0.12 update ([#93](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/93))
+
+
+
+## [v1.24.0] - 2019-06-06
+
+- Added placement groups ([#94](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/94))
+- Revert example
+- Added placement groups
+
+
+
+## [v1.23.0] - 2019-06-06
+
+- Add volume tags naming and output ([#82](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/82))
+
+
+
+## [v2.0.0] - 2019-06-06
+
+- Terraform 0.12 update ([#93](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/93))
+
+
+
+## [v1.22.0] - 2019-06-06
+
+- Update module to the current version ([#88](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/88))
+
+
+
+## [v1.21.0] - 2019-03-22
+
+- Fix formatting
+- examples/basic/main.tf: Add usage of "root_block_device" ([#18](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/18)) ([#65](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/65))
+
+
+
+## [v1.20.0] - 2019-03-22
+
+- Fix formatting
+- main.tf: Make number of instances created configurable, defaulting to 1 ([#64](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/64))
+- Add missing required field ([#81](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/81))
+
+
+
+## [v1.19.0] - 2019-03-01
+
+- Fixed readme after [#76](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/76)
+- network_interface_id Attribute Removal ([#76](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/76))
+
+
+
+## [v1.18.0] - 2019-02-27
+
+- fix count variables are only valid within resources ([#72](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/72))
+
+
+
+## [v1.17.0] - 2019-02-25
+
+- fix call to local.instance_name ([#71](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/71))
+
+
+
+## [v1.16.0] - 2019-02-25
+
+- Fixed readme
+- Ability to append numerical suffix even to 1 instance ([#70](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/70))
+
+
+
+## [v1.15.0] - 2019-02-21
+
+- Allow multiple subnet ids ([#67](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/67))
+
+
+
+## [v1.14.0] - 2019-01-26
+
+- Tags should be possible to override (fixed [#53](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/53)) ([#66](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/66))
+- fix typo ([#61](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/61))
+
+
+
+## [v1.13.0] - 2018-10-31
+
+- Include the module version and some code formatting ([#52](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/52))
+
+
+
+## [v1.12.0] - 2018-10-06
+
+- Fixed [#51](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/51). t2 and t3 instances can be unlimited
+
+
+
+## [v1.11.0] - 2018-09-04
+
+- Added example of EBS volume attachment (related to [#46](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/46)) ([#47](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/47))
+- Ignore changes in the ebs_block_device ([#46](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/46))
+
+
+
+## [v1.10.0] - 2018-08-18
+
+- [master]: Narrow t2 selection criteria. ([#44](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/44))
+
+
+
+## [v1.9.0] - 2018-06-08
+
+- Fixed t2-unlimited bug (related issue [#35](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/35)) ([#37](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/37))
+
+
+
+## [v1.8.0] - 2018-06-04
+
+- Added support for CPU credits ([#35](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/35))
+
+
+
+## [v1.7.0] - 2018-06-02
+
+- Added encrypted AMI info ([#34](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/34))
+
+
+
+## [v1.6.0] - 2018-05-16
+
+- Added pre-commit hook to autogenerate terraform-docs ([#33](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/33))
+
+
+
+## [v1.5.0] - 2018-04-04
+
+- Minor formatting fix
+- Modify tag name management to add -%d suffixe only if instance_count > 1
+
+
+
+## [v1.4.0] - 2018-04-04
+
+- Stop ignoring changes in vpc_security_group_ids
+
+
+
+## [v1.3.0] - 2018-03-06
+
+- Renamed count to instance_count (fixes [#23](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/23))
+- Fix: add missing variable to the usage example
+
+
+
+## [v1.2.1] - 2018-03-01
+
+- Added aws_eip to example and pre-commit hooks
+
+
+
+## [v1.2.0] - 2018-01-19
+
+- Add tags to output variables
+
+
+
+## [v1.1.0] - 2017-12-08
+
+- Make module idempotent by requiring subnet_id and ignore changes in several arguments (fixes [#10](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/10))
+
+
+
+## [v1.0.4] - 2017-11-21
+
+- Removed placement_group from outputs
+
+
+
+## [v1.0.3] - 2017-11-15
+
+- Fix incorrect subnet_id output expression
+- Updated example with all-icmp security group rule
+
+
+
+## [v1.0.2] - 2017-10-13
+
+- Added example with security-group module
+
+
+
+## [v1.0.1] - 2017-09-14
+
+- Updated example and made security group required
+
+
+
+## v1.0.0 - 2017-09-12
+
+- Updated repo name
+- Updated README
+- Added complete code with example and READMEs
+- Initial commit
+
+
+[Unreleased]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.2.0...HEAD
+[v3.2.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.1.0...v3.2.0
+[v3.1.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v3.0.0...v3.1.0
+[v3.0.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.21.0...v3.0.0
+[v2.21.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.20.0...v2.21.0
+[v2.20.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.19.0...v2.20.0
+[v2.19.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.18.0...v2.19.0
+[v2.18.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.17.0...v2.18.0
+[v2.17.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.16.0...v2.17.0
+[v2.16.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.15.0...v2.16.0
+[v2.15.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.14.0...v2.15.0
+[v2.14.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.25.0...v2.14.0
+[v1.25.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.13.0...v1.25.0
+[v2.13.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.12.0...v2.13.0
+[v2.12.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.11.0...v2.12.0
+[v2.11.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.10.0...v2.11.0
+[v2.10.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.9.0...v2.10.0
+[v2.9.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.8.0...v2.9.0
+[v2.8.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.7.0...v2.8.0
+[v2.7.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.6.0...v2.7.0
+[v2.6.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.5.0...v2.6.0
+[v2.5.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.4.0...v2.5.0
+[v2.4.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.3.0...v2.4.0
+[v2.3.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.2.0...v2.3.0
+[v2.2.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.1.0...v2.2.0
+[v2.1.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.24.0...v2.1.0
+[v1.24.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.23.0...v1.24.0
+[v1.23.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v2.0.0...v1.23.0
+[v2.0.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.22.0...v2.0.0
+[v1.22.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.21.0...v1.22.0
+[v1.21.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.20.0...v1.21.0
+[v1.20.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.19.0...v1.20.0
+[v1.19.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.18.0...v1.19.0
+[v1.18.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.17.0...v1.18.0
+[v1.17.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.16.0...v1.17.0
+[v1.16.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.15.0...v1.16.0
+[v1.15.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.14.0...v1.15.0
+[v1.14.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.13.0...v1.14.0
+[v1.13.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.12.0...v1.13.0
+[v1.12.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.11.0...v1.12.0
+[v1.11.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.10.0...v1.11.0
+[v1.10.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.9.0...v1.10.0
+[v1.9.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.8.0...v1.9.0
+[v1.8.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.7.0...v1.8.0
+[v1.7.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.6.0...v1.7.0
+[v1.6.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.5.0...v1.6.0
+[v1.5.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.4.0...v1.5.0
+[v1.4.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.3.0...v1.4.0
+[v1.3.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.2.1...v1.3.0
+[v1.2.1]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.2.0...v1.2.1
+[v1.2.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.1.0...v1.2.0
+[v1.1.0]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.0.4...v1.1.0
+[v1.0.4]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.0.3...v1.0.4
+[v1.0.3]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.0.2...v1.0.3
+[v1.0.2]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.0.1...v1.0.2
+[v1.0.1]: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/compare/v1.0.0...v1.0.1
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..d9a10c0d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
index 8d7640e7..a7af2b00 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,303 @@
-# sparkjava-war-example
-Build war with maven and sparkjava framework
-
-Steps:
-
-1. Download a fresh [Tomcat 8 distribution](https://tomcat.apache.org/download-80.cgi)
-2. Clone this repository to your local machine
-3. Run mvn package
-4. Copy the generated `sparkjava-hello-world-1.0.war` to the Tomcat `webapps` folder
-5. Start Tomcat by running `bin\startup.bat` (or `bin\startaup.sh` for Linux)
-5. Tomcat will automatically deploy the war
-6. Open [http://localhost:8080/sparkjava-hello-world-1.0/hello](http://localhost:8080/sparkjava-hello-world-1.0/hello) in your browser
+# AWS EC2 Instance Terraform module
+
+Terraform module which creates an EC2 instance on AWS.
+
+[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
+
+## Usage
+
+### Single EC2 Instance
+
+```hcl
+module "ec2_instance" {
+ source = "terraform-aws-modules/ec2-instance/aws"
+ version = "~> 3.0"
+
+ name = "single-instance"
+
+ ami = "ami-ebd02392"
+ instance_type = "t2.micro"
+ key_name = "user1"
+ monitoring = true
+ vpc_security_group_ids = ["sg-12345678"]
+ subnet_id = "subnet-eddcdzz4"
+
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+}
+```
+
+### Multiple EC2 Instance
+
+```hcl
+module "ec2_instance" {
+ source = "terraform-aws-modules/ec2-instance/aws"
+ version = "~> 3.0"
+
+ for_each = toset(["one", "two", "three"])
+
+ name = "instance-${each.key}"
+
+ ami = "ami-ebd02392"
+ instance_type = "t2.micro"
+ key_name = "user1"
+ monitoring = true
+ vpc_security_group_ids = ["sg-12345678"]
+ subnet_id = "subnet-eddcdzz4"
+
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+}
+```
+
+### Spot EC2 Instance
+
+```hcl
+module "ec2_instance" {
+ source = "terraform-aws-modules/ec2-instance/aws"
+ version = "~> 3.0"
+
+ name = "spot-instance"
+
+ create_spot_instance = true
+ spot_price = "0.60"
+ spot_type = "persistent"
+
+ ami = "ami-ebd02392"
+ instance_type = "t2.micro"
+ key_name = "user1"
+ monitoring = true
+ vpc_security_group_ids = ["sg-12345678"]
+ subnet_id = "subnet-eddcdzz4"
+
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+}
+```
+
+## Module wrappers
+
+Users of this Terraform module can create multiple similar resources by using [`for_each` meta-argument within `module` block](https://www.terraform.io/language/meta-arguments/for_each) which became available in Terraform 0.13.
+
+Users of Terragrunt can achieve similar results by using modules provided in the [wrappers](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/wrappers) directory, if they prefer to reduce amount of configuration files.
+
+## Examples
+
+- [Complete EC2 instance](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/complete)
+- [EC2 instance with EBS volume attachment](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/volume-attachment)
+
+## Make an encrypted AMI for use
+
+This module does not support encrypted AMI's out of the box however it is easy enough for you to generate one for use
+
+This example creates an encrypted image from the latest ubuntu 16.04 base image.
+
+```hcl
+provider "aws" {
+ region = "us-west-2"
+}
+
+data "aws_ami" "ubuntu" {
+ most_recent = true
+ owners = ["679593333241"]
+
+ filter {
+ name = "name"
+ values = ["ubuntu-minimal/images/hvm-ssd/ubuntu-focal-20.04-*"]
+ }
+
+ filter {
+ name = "virtualization-type"
+ values = ["hvm"]
+ }
+}
+
+resource "aws_ami_copy" "ubuntu_encrypted_ami" {
+ name = "ubuntu-encrypted-ami"
+ description = "An encrypted root ami based off ${data.aws_ami.ubuntu.id}"
+ source_ami_id = data.aws_ami.ubuntu.id
+ source_ami_region = "eu-west-2"
+ encrypted = true
+
+ tags = { Name = "ubuntu-encrypted-ami" }
+}
+
+data "aws_ami" "encrypted-ami" {
+ most_recent = true
+
+ filter {
+ name = "name"
+ values = [aws_ami_copy.ubuntu_encrypted_ami.id]
+ }
+
+ owners = ["self"]
+}
+```
+
+## Conditional creation
+
+The following combinations are supported to conditionally create resources:
+
+- Disable resource creation (no resources created):
+
+```hcl
+ create = false
+```
+
+- Create spot instance:
+
+```hcl
+ create_spot_instance = true
+```
+
+## Notes
+
+- `network_interface` can't be specified together with `vpc_security_group_ids`, `associate_public_ip_address`, `subnet_id`. See [complete example](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/complete) for details.
+- Changes in `ebs_block_device` argument will be ignored. Use [aws_volume_attachment](https://www.terraform.io/docs/providers/aws/r/volume_attachment.html) resource to attach and detach volumes from AWS EC2 instances. See [this example](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/volume-attachment).
+- In regards to spot instances, you must grant the `AWSServiceRoleForEC2Spot` service-linked role access to any custom KMS keys, otherwise your spot request and instances will fail with `bad parameters`. You can see more details about why the request failed by using the awscli and `aws ec2 describe-spot-instance-requests`
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 0.13.1 |
+| [aws](#requirement\_aws) | >= 4.20.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 4.20.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
+| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_instance.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
+| [aws_spot_instance_request.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/spot_instance_request) | resource |
+| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
+| [aws_ssm_parameter.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [ami](#input\_ami) | ID of AMI to use for the instance | `string` | `null` | no |
+| [ami\_ssm\_parameter](#input\_ami\_ssm\_parameter) | SSM parameter name for the AMI ID. For Amazon Linux AMI SSM parameters see [reference](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-public-parameters-ami.html) | `string` | `"/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"` | no |
+| [associate\_public\_ip\_address](#input\_associate\_public\_ip\_address) | Whether to associate a public IP address with an instance in a VPC | `bool` | `null` | no |
+| [availability\_zone](#input\_availability\_zone) | AZ to start the instance in | `string` | `null` | no |
+| [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Describes an instance's Capacity Reservation targeting option | `any` | `{}` | no |
+| [cpu\_core\_count](#input\_cpu\_core\_count) | Sets the number of CPU cores for an instance. | `number` | `null` | no |
+| [cpu\_credits](#input\_cpu\_credits) | The credit option for CPU usage (unlimited or standard) | `string` | `null` | no |
+| [cpu\_threads\_per\_core](#input\_cpu\_threads\_per\_core) | Sets the number of CPU threads per core for an instance (has no effect unless cpu\_core\_count is also set). | `number` | `null` | no |
+| [create](#input\_create) | Whether to create an instance | `bool` | `true` | no |
+| [create\_iam\_instance\_profile](#input\_create\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `false` | no |
+| [create\_spot\_instance](#input\_create\_spot\_instance) | Depicts if the instance is a spot instance | `bool` | `false` | no |
+| [disable\_api\_stop](#input\_disable\_api\_stop) | If true, enables EC2 Instance Stop Protection. | `bool` | `null` | no |
+| [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 Instance Termination Protection | `bool` | `null` | no |
+| [ebs\_block\_device](#input\_ebs\_block\_device) | Additional EBS block devices to attach to the instance | `list(map(string))` | `[]` | no |
+| [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance will be EBS-optimized | `bool` | `null` | no |
+| [enable\_volume\_tags](#input\_enable\_volume\_tags) | Whether to enable volume tags (if enabled it conflicts with root\_block\_device tags) | `bool` | `true` | no |
+| [enclave\_options\_enabled](#input\_enclave\_options\_enabled) | Whether Nitro Enclaves will be enabled on the instance. Defaults to `false` | `bool` | `null` | no |
+| [ephemeral\_block\_device](#input\_ephemeral\_block\_device) | Customize Ephemeral (also known as Instance Store) volumes on the instance | `list(map(string))` | `[]` | no |
+| [get\_password\_data](#input\_get\_password\_data) | If true, wait for password data to become available and retrieve it. | `bool` | `null` | no |
+| [hibernation](#input\_hibernation) | If true, the launched EC2 instance will support hibernation | `bool` | `null` | no |
+| [host\_id](#input\_host\_id) | ID of a dedicated host that the instance will be assigned to. Use when an instance is to be launched on a specific dedicated host | `string` | `null` | no |
+| [iam\_instance\_profile](#input\_iam\_instance\_profile) | IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile | `string` | `null` | no |
+| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `null` | no |
+| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [iam\_role\_policies](#input\_iam\_role\_policies) | Policies attached to the IAM role | `map(string)` | `{}` | no |
+| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role/profile created | `map(string)` | `{}` | no |
+| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name` or `name`) is used as a prefix | `bool` | `true` | no |
+| [instance\_initiated\_shutdown\_behavior](#input\_instance\_initiated\_shutdown\_behavior) | Shutdown behavior for the instance. Amazon defaults this to stop for EBS-backed instances and terminate for instance-store instances. Cannot be set on instance-store instance | `string` | `null` | no |
+| [instance\_type](#input\_instance\_type) | The type of instance to start | `string` | `"t3.micro"` | no |
+| [ipv6\_address\_count](#input\_ipv6\_address\_count) | A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet | `number` | `null` | no |
+| [ipv6\_addresses](#input\_ipv6\_addresses) | Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface | `list(string)` | `null` | no |
+| [key\_name](#input\_key\_name) | Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource | `string` | `null` | no |
+| [launch\_template](#input\_launch\_template) | Specifies a Launch Template to configure the instance. Parameters configured on this resource will override the corresponding parameters in the Launch Template | `map(string)` | `null` | no |
+| [metadata\_options](#input\_metadata\_options) | Customize the metadata options of the instance | `map(string)` | `{}` | no |
+| [monitoring](#input\_monitoring) | If true, the launched EC2 instance will have detailed monitoring enabled | `bool` | `false` | no |
+| [name](#input\_name) | Name to be used on EC2 instance created | `string` | `""` | no |
+| [network\_interface](#input\_network\_interface) | Customize network interfaces to be attached at instance boot time | `list(map(string))` | `[]` | no |
+| [placement\_group](#input\_placement\_group) | The Placement Group to start the instance in | `string` | `null` | no |
+| [private\_ip](#input\_private\_ip) | Private IP address to associate with the instance in a VPC | `string` | `null` | no |
+| [putin\_khuylo](#input\_putin\_khuylo) | Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo! | `bool` | `true` | no |
+| [root\_block\_device](#input\_root\_block\_device) | Customize details about the root block device of the instance. See Block Devices below for details | `list(any)` | `[]` | no |
+| [secondary\_private\_ips](#input\_secondary\_private\_ips) | A list of secondary private IPv4 addresses to assign to the instance's primary network interface (eth0) in a VPC. Can only be assigned to the primary network interface (eth0) attached at instance creation, not a pre-existing network interface i.e. referenced in a `network_interface block` | `list(string)` | `null` | no |
+| [source\_dest\_check](#input\_source\_dest\_check) | Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs. | `bool` | `true` | no |
+| [spot\_block\_duration\_minutes](#input\_spot\_block\_duration\_minutes) | The required duration for the Spot instances, in minutes. This value must be a multiple of 60 (60, 120, 180, 240, 300, or 360) | `number` | `null` | no |
+| [spot\_instance\_interruption\_behavior](#input\_spot\_instance\_interruption\_behavior) | Indicates Spot instance behavior when it is interrupted. Valid values are `terminate`, `stop`, or `hibernate` | `string` | `null` | no |
+| [spot\_launch\_group](#input\_spot\_launch\_group) | A launch group is a group of spot instances that launch together and terminate together. If left empty instances are launched and terminated individually | `string` | `null` | no |
+| [spot\_price](#input\_spot\_price) | The maximum price to request on the spot market. Defaults to on-demand price | `string` | `null` | no |
+| [spot\_type](#input\_spot\_type) | If set to one-time, after the instance is terminated, the spot request will be closed. Default `persistent` | `string` | `null` | no |
+| [spot\_valid\_from](#input\_spot\_valid\_from) | The start date and time of the request, in UTC RFC3339 format(for example, YYYY-MM-DDTHH:MM:SSZ) | `string` | `null` | no |
+| [spot\_valid\_until](#input\_spot\_valid\_until) | The end date and time of the request, in UTC RFC3339 format(for example, YYYY-MM-DDTHH:MM:SSZ) | `string` | `null` | no |
+| [spot\_wait\_for\_fulfillment](#input\_spot\_wait\_for\_fulfillment) | If set, Terraform will wait for the Spot Request to be fulfilled, and will throw an error if the timeout of 10m is reached | `bool` | `null` | no |
+| [subnet\_id](#input\_subnet\_id) | The VPC Subnet ID to launch in | `string` | `null` | no |
+| [tags](#input\_tags) | A mapping of tags to assign to the resource | `map(string)` | `{}` | no |
+| [tenancy](#input\_tenancy) | The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host. | `string` | `null` | no |
+| [timeouts](#input\_timeouts) | Define maximum timeout for creating, updating, and deleting EC2 instance resources | `map(string)` | `{}` | no |
+| [user\_data](#input\_user\_data) | The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user\_data\_base64 instead. | `string` | `null` | no |
+| [user\_data\_base64](#input\_user\_data\_base64) | Can be used instead of user\_data to pass base64-encoded binary data directly. Use this instead of user\_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption. | `string` | `null` | no |
+| [user\_data\_replace\_on\_change](#input\_user\_data\_replace\_on\_change) | When used in combination with user\_data or user\_data\_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set. | `bool` | `false` | no |
+| [volume\_tags](#input\_volume\_tags) | A mapping of tags to assign to the devices created by the instance at launch time | `map(string)` | `{}` | no |
+| [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of security group IDs to associate with | `list(string)` | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [arn](#output\_arn) | The ARN of the instance |
+| [capacity\_reservation\_specification](#output\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [iam\_instance\_profile\_arn](#output\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | Instance profile's ID |
+| [iam\_instance\_profile\_unique](#output\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role |
+| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [id](#output\_id) | The ID of the instance |
+| [instance\_state](#output\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ipv6\_addresses](#output\_ipv6\_addresses) | The IPv6 address assigned to the instance, if applicable. |
+| [outpost\_arn](#output\_outpost\_arn) | The ARN of the Outpost the instance is assigned to |
+| [password\_data](#output\_password\_data) | Base-64 encoded encrypted password data for the instance. Useful for getting the administrator password for instances running Microsoft Windows. This attribute is only exported if `get_password_data` is true |
+| [primary\_network\_interface\_id](#output\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [private\_dns](#output\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [private\_ip](#output\_private\_ip) | The private IP address assigned to the instance. |
+| [public\_dns](#output\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [public\_ip](#output\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [spot\_bid\_status](#output\_spot\_bid\_status) | The current bid status of the Spot Instance Request |
+| [spot\_instance\_id](#output\_spot\_instance\_id) | The Instance ID (if any) that is currently fulfilling the Spot Instance request |
+| [spot\_request\_state](#output\_spot\_request\_state) | The current request state of the Spot Instance Request |
+| [tags\_all](#output\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+
+
+## Authors
+
+Module is maintained by [Anton Babenko](https://github.com/antonbabenko) with help from [these awesome contributors](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/graphs/contributors).
+
+## License
+
+Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/LICENSE) for full details.
+
+## Additional information for users from Russia and Belarus
+
+* Russia has [illegally annexed Crimea in 2014](https://en.wikipedia.org/wiki/Annexation_of_Crimea_by_the_Russian_Federation) and [brought the war in Donbas](https://en.wikipedia.org/wiki/War_in_Donbas) followed by [full-scale invasion of Ukraine in 2022](https://en.wikipedia.org/wiki/2022_Russian_invasion_of_Ukraine).
+* Russia has brought sorrow and devastations to millions of Ukrainians, killed hundreds of innocent people, damaged thousands of buildings, and forced several million people to flee.
+* [Putin khuylo!](https://en.wikipedia.org/wiki/Putin_khuylo!)
diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md
new file mode 100644
index 00000000..46175fb0
--- /dev/null
+++ b/UPGRADE-3.0.md
@@ -0,0 +1,123 @@
+# Upgrade from v2.x to v3.x
+
+If you have any questions regarding this upgrade process, please consult the `examples` directory:
+
+- [Complete](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/complete)
+- [Volume Attachment](https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/tree/master/examples/volume-attachment)
+
+If you find a bug, please open an issue with supporting configuration to reproduce.
+
+## List of backwards incompatible changes
+
+- Terraform v0.13.1 is now minimum supported version to take advantage of `count` and `for_each` arguments at module level
+
+### Variable and output changes
+
+1. Removed variables:
+
+ - `instance_count`
+ - `subnet_ids` (only need to use `subnet_id` now)
+ - `private_ips` (only need to use `private_ip` now)
+ - `use_num_suffix`
+ - `num_suffix_format`
+
+2. Renamed variables:
+
+ - `tags` -> `tags_all`
+
+3. Removed outputs:
+
+ - `availability_zone`
+ - `placement_group`
+ - `key_name`
+ - `ipv6_addresses`
+ - `private_ip`
+ - `security_groups`
+ - `vpc_security_group_ids`
+ - `subnet_id`
+ - `credit_specification`
+ - `metadata_options`
+ - `root_block_device_volume_ids`
+ - `ebs_block_device_volume_ids`
+ - `volume_tags`
+ - `instance_count`
+
+4. Renamed outputs:
+
+ :info: All outputs used to be lists, and are now singular outputs due to the removal of `count`
+
+## Upgrade State Migrations
+
+### Before 2.x Example
+
+```hcl
+module "ec2_upgrade" {
+ source = "terraform-aws-modules/ec2-instance/aws"
+ version = "2.21.0"
+
+ instance_count = 3
+
+ name = local.name
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = "c5.large"
+ subnet_ids = module.vpc.private_subnets
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ tags = local.tags
+}
+```
+
+### After 3.x Example
+
+```hcl
+locals {
+ num_suffix_format = "-%d"
+ multiple_instances = {
+ 0 = {
+ num_suffix = 1
+ instance_type = "c5.large"
+ subnet_id = element(module.vpc.private_subnets, 0)
+ }
+ 1 = {
+ num_suffix = 2
+ instance_type = "c5.large"
+ subnet_id = element(module.vpc.private_subnets, 1)
+ }
+ 2 = {
+ num_suffix = 3
+ instance_type = "c5.large"
+ subnet_id = element(module.vpc.private_subnets, 2)
+ }
+ }
+}
+
+module "ec2_upgrade" {
+ source = "../../"
+
+ for_each = local.multiple_instances
+
+ name = format("%s${local.num_suffix_format}", local.name, each.value.num_suffix)
+
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = each.value.instance_type
+ subnet_id = each.value.subnet_id
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ tags = local.tags
+}
+```
+
+To migrate from the `v2.x` version to `v3.x` version example shown above, the following state move commands can be performed to maintain the current resources without modification:
+
+```bash
+terraform state mv 'module.ec2_upgrade.aws_instance.this[0]' 'module.ec2_upgrade["0"].aws_instance.this[0]'
+terraform state mv 'module.ec2_upgrade.aws_instance.this[1]' 'module.ec2_upgrade["1"].aws_instance.this[0]'
+terraform state mv 'module.ec2_upgrade.aws_instance.this[2]' 'module.ec2_upgrade["2"].aws_instance.this[0]'
+```
+
+:info: Notes
+
+- In the `v2.x` example we use `subnet_ids` which is an array of subnets. These are mapped to the respective instance based on their index location; therefore in the `v3.x` example we are doing a similar index lookup to map back to the existing subnet used for that instance. This would also be the case for `private_ips`
+- In the `v3.x` example we have shown how users can continue to use the same naming scheme that is currently in use by the `v2.x` module. By moving the `num_suffix_format` into the module name itself inside a format function, users can continue to customize the names generated in a similar manner as that of the `v2.x` module.
diff --git a/examples/complete/README.md b/examples/complete/README.md
new file mode 100644
index 00000000..99e48366
--- /dev/null
+++ b/examples/complete/README.md
@@ -0,0 +1,113 @@
+# Complete EC2 instance
+
+Configuration in this directory creates EC2 instances with different sets of arguments (with Elastic IP, with network interface attached, with credit specifications).
+
+## Usage
+
+To run this example you need to execute:
+
+```bash
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+Note that this example may create resources which can cost money. Run `terraform destroy` when you don't need these resources.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 0.13.1 |
+| [aws](#requirement\_aws) | >= 4.7 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 4.7 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [ec2\_complete](#module\_ec2\_complete) | ../../ | n/a |
+| [ec2\_disabled](#module\_ec2\_disabled) | ../../ | n/a |
+| [ec2\_metadata\_options](#module\_ec2\_metadata\_options) | ../../ | n/a |
+| [ec2\_multiple](#module\_ec2\_multiple) | ../../ | n/a |
+| [ec2\_network\_interface](#module\_ec2\_network\_interface) | ../../ | n/a |
+| [ec2\_open\_capacity\_reservation](#module\_ec2\_open\_capacity\_reservation) | ../../ | n/a |
+| [ec2\_spot\_instance](#module\_ec2\_spot\_instance) | ../../ | n/a |
+| [ec2\_t2\_unlimited](#module\_ec2\_t2\_unlimited) | ../../ | n/a |
+| [ec2\_t3\_unlimited](#module\_ec2\_t3\_unlimited) | ../../ | n/a |
+| [ec2\_targeted\_capacity\_reservation](#module\_ec2\_targeted\_capacity\_reservation) | ../../ | n/a |
+| [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 |
+| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_ec2_capacity_reservation.open](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_capacity_reservation) | resource |
+| [aws_ec2_capacity_reservation.targeted](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_capacity_reservation) | resource |
+| [aws_kms_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
+| [aws_network_interface.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_interface) | resource |
+| [aws_placement_group.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/placement_group) | resource |
+| [aws_ami.amazon_linux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source |
+
+## Inputs
+
+No inputs.
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [ec2\_complete\_arn](#output\_ec2\_complete\_arn) | The ARN of the instance |
+| [ec2\_complete\_capacity\_reservation\_specification](#output\_ec2\_complete\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [ec2\_complete\_iam\_instance\_profile\_arn](#output\_ec2\_complete\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [ec2\_complete\_iam\_instance\_profile\_id](#output\_ec2\_complete\_iam\_instance\_profile\_id) | Instance profile's ID |
+| [ec2\_complete\_iam\_instance\_profile\_unique](#output\_ec2\_complete\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [ec2\_complete\_iam\_role\_arn](#output\_ec2\_complete\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [ec2\_complete\_iam\_role\_name](#output\_ec2\_complete\_iam\_role\_name) | The name of the IAM role |
+| [ec2\_complete\_iam\_role\_unique\_id](#output\_ec2\_complete\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [ec2\_complete\_id](#output\_ec2\_complete\_id) | The ID of the instance |
+| [ec2\_complete\_instance\_state](#output\_ec2\_complete\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ec2\_complete\_primary\_network\_interface\_id](#output\_ec2\_complete\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [ec2\_complete\_private\_dns](#output\_ec2\_complete\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_complete\_public\_dns](#output\_ec2\_complete\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_complete\_public\_ip](#output\_ec2\_complete\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [ec2\_complete\_tags\_all](#output\_ec2\_complete\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+| [ec2\_multiple](#output\_ec2\_multiple) | The full output of the `ec2_module` module |
+| [ec2\_spot\_instance\_arn](#output\_ec2\_spot\_instance\_arn) | The ARN of the instance |
+| [ec2\_spot\_instance\_capacity\_reservation\_specification](#output\_ec2\_spot\_instance\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [ec2\_spot\_instance\_id](#output\_ec2\_spot\_instance\_id) | The ID of the instance |
+| [ec2\_spot\_instance\_instance\_state](#output\_ec2\_spot\_instance\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ec2\_spot\_instance\_primary\_network\_interface\_id](#output\_ec2\_spot\_instance\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [ec2\_spot\_instance\_private\_dns](#output\_ec2\_spot\_instance\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_spot\_instance\_public\_dns](#output\_ec2\_spot\_instance\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_spot\_instance\_public\_ip](#output\_ec2\_spot\_instance\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [ec2\_spot\_instance\_tags\_all](#output\_ec2\_spot\_instance\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+| [ec2\_t2\_unlimited\_arn](#output\_ec2\_t2\_unlimited\_arn) | The ARN of the instance |
+| [ec2\_t2\_unlimited\_capacity\_reservation\_specification](#output\_ec2\_t2\_unlimited\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [ec2\_t2\_unlimited\_id](#output\_ec2\_t2\_unlimited\_id) | The ID of the instance |
+| [ec2\_t2\_unlimited\_instance\_state](#output\_ec2\_t2\_unlimited\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ec2\_t2\_unlimited\_primary\_network\_interface\_id](#output\_ec2\_t2\_unlimited\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [ec2\_t2\_unlimited\_private\_dns](#output\_ec2\_t2\_unlimited\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_t2\_unlimited\_public\_dns](#output\_ec2\_t2\_unlimited\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_t2\_unlimited\_public\_ip](#output\_ec2\_t2\_unlimited\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [ec2\_t2\_unlimited\_tags\_all](#output\_ec2\_t2\_unlimited\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+| [ec2\_t3\_unlimited\_arn](#output\_ec2\_t3\_unlimited\_arn) | The ARN of the instance |
+| [ec2\_t3\_unlimited\_capacity\_reservation\_specification](#output\_ec2\_t3\_unlimited\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [ec2\_t3\_unlimited\_id](#output\_ec2\_t3\_unlimited\_id) | The ID of the instance |
+| [ec2\_t3\_unlimited\_instance\_state](#output\_ec2\_t3\_unlimited\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ec2\_t3\_unlimited\_primary\_network\_interface\_id](#output\_ec2\_t3\_unlimited\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [ec2\_t3\_unlimited\_private\_dns](#output\_ec2\_t3\_unlimited\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_t3\_unlimited\_public\_dns](#output\_ec2\_t3\_unlimited\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_t3\_unlimited\_public\_ip](#output\_ec2\_t3\_unlimited\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [ec2\_t3\_unlimited\_tags\_all](#output\_ec2\_t3\_unlimited\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+| [spot\_bid\_status](#output\_spot\_bid\_status) | The current bid status of the Spot Instance Request |
+| [spot\_instance\_id](#output\_spot\_instance\_id) | The Instance ID (if any) that is currently fulfilling the Spot Instance request |
+| [spot\_request\_state](#output\_spot\_request\_state) | The current request state of the Spot Instance Request |
+
diff --git a/examples/complete/main.tf b/examples/complete/main.tf
new file mode 100644
index 00000000..301931b6
--- /dev/null
+++ b/examples/complete/main.tf
@@ -0,0 +1,378 @@
+provider "aws" {
+ region = local.region
+}
+
+locals {
+ name = "example-ec2-complete"
+ region = "eu-west-1"
+
+ user_data = <<-EOT
+ #!/bin/bash
+ echo "Hello Terraform!"
+ EOT
+
+ tags = {
+ Owner = "user"
+ Environment = "dev"
+ }
+}
+
+################################################################################
+# EC2 Module
+################################################################################
+
+module "ec2_disabled" {
+ source = "../../"
+
+ create = false
+}
+
+module "ec2_complete" {
+ source = "../../"
+
+ name = local.name
+
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = "c5.xlarge" # used to set core count below
+ availability_zone = element(module.vpc.azs, 0)
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ placement_group = aws_placement_group.web.id
+ associate_public_ip_address = true
+ disable_api_stop = false
+
+ create_iam_instance_profile = true
+ iam_role_description = "IAM role for EC2 instance"
+ iam_role_policies = {
+ AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess"
+ }
+
+ # only one of these can be enabled at a time
+ hibernation = true
+ # enclave_options_enabled = true
+
+ user_data_base64 = base64encode(local.user_data)
+ user_data_replace_on_change = true
+
+ cpu_core_count = 2 # default 4
+ cpu_threads_per_core = 1 # default 2
+
+ enable_volume_tags = false
+ root_block_device = [
+ {
+ encrypted = true
+ volume_type = "gp3"
+ throughput = 200
+ volume_size = 50
+ tags = {
+ Name = "my-root-block"
+ }
+ },
+ ]
+
+ ebs_block_device = [
+ {
+ device_name = "/dev/sdf"
+ volume_type = "gp3"
+ volume_size = 5
+ throughput = 200
+ encrypted = true
+ kms_key_id = aws_kms_key.this.arn
+ }
+ ]
+
+ tags = local.tags
+}
+
+module "ec2_network_interface" {
+ source = "../../"
+
+ name = "${local.name}-network-interface"
+
+ network_interface = [
+ {
+ device_index = 0
+ network_interface_id = aws_network_interface.this.id
+ delete_on_termination = false
+ }
+ ]
+
+ tags = local.tags
+}
+
+module "ec2_metadata_options" {
+ source = "../../"
+
+ name = "${local.name}-metadata-options"
+
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+
+ metadata_options = {
+ http_endpoint = "enabled"
+ http_tokens = "required"
+ http_put_response_hop_limit = 8
+ instance_metadata_tags = "enabled"
+ }
+
+ tags = local.tags
+}
+
+module "ec2_t2_unlimited" {
+ source = "../../"
+
+ name = "${local.name}-t2-unlimited"
+
+ instance_type = "t2.micro"
+ cpu_credits = "unlimited"
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ tags = local.tags
+}
+
+module "ec2_t3_unlimited" {
+ source = "../../"
+
+ name = "${local.name}-t3-unlimited"
+
+ instance_type = "t3.micro"
+ cpu_credits = "unlimited"
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ tags = local.tags
+}
+
+################################################################################
+# EC2 Module - multiple instances with `for_each`
+################################################################################
+
+locals {
+ multiple_instances = {
+ one = {
+ instance_type = "t3.micro"
+ availability_zone = element(module.vpc.azs, 0)
+ subnet_id = element(module.vpc.private_subnets, 0)
+ root_block_device = [
+ {
+ encrypted = true
+ volume_type = "gp3"
+ throughput = 200
+ volume_size = 50
+ tags = {
+ Name = "my-root-block"
+ }
+ }
+ ]
+ }
+ two = {
+ instance_type = "t3.small"
+ availability_zone = element(module.vpc.azs, 1)
+ subnet_id = element(module.vpc.private_subnets, 1)
+ root_block_device = [
+ {
+ encrypted = true
+ volume_type = "gp2"
+ volume_size = 50
+ }
+ ]
+ }
+ three = {
+ instance_type = "t3.medium"
+ availability_zone = element(module.vpc.azs, 2)
+ subnet_id = element(module.vpc.private_subnets, 2)
+ }
+ }
+}
+
+module "ec2_multiple" {
+ source = "../../"
+
+ for_each = local.multiple_instances
+
+ name = "${local.name}-multi-${each.key}"
+
+ instance_type = each.value.instance_type
+ availability_zone = each.value.availability_zone
+ subnet_id = each.value.subnet_id
+ vpc_security_group_ids = [module.security_group.security_group_id]
+
+ enable_volume_tags = false
+ root_block_device = lookup(each.value, "root_block_device", [])
+
+ tags = local.tags
+}
+
+################################################################################
+# EC2 Module - spot instance request
+################################################################################
+
+module "ec2_spot_instance" {
+ source = "../../"
+
+ name = "${local.name}-spot-instance"
+ create_spot_instance = true
+
+ availability_zone = element(module.vpc.azs, 0)
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ # Spot request specific attributes
+ spot_price = "0.1"
+ spot_wait_for_fulfillment = true
+ spot_type = "persistent"
+ spot_instance_interruption_behavior = "terminate"
+ # End spot request specific attributes
+
+ user_data_base64 = base64encode(local.user_data)
+
+ cpu_core_count = 2 # default 4
+ cpu_threads_per_core = 1 # default 2
+
+
+ enable_volume_tags = false
+ root_block_device = [
+ {
+ encrypted = true
+ volume_type = "gp3"
+ throughput = 200
+ volume_size = 50
+ tags = {
+ Name = "my-root-block"
+ }
+ },
+ ]
+
+ ebs_block_device = [
+ {
+ device_name = "/dev/sdf"
+ volume_type = "gp3"
+ volume_size = 5
+ throughput = 200
+ encrypted = true
+ # kms_key_id = aws_kms_key.this.arn # you must grant the AWSServiceRoleForEC2Spot service-linked role access to any custom KMS keys
+ }
+ ]
+
+ tags = local.tags
+}
+
+################################################################################
+# EC2 Module - Capacity Reservation
+################################################################################
+
+module "ec2_open_capacity_reservation" {
+ source = "../../"
+
+ name = "${local.name}-open-capacity-reservation"
+
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = "t3.micro"
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = false
+
+ capacity_reservation_specification = {
+ capacity_reservation_target = {
+ capacity_reservation_id = aws_ec2_capacity_reservation.open.id
+ }
+ }
+
+ tags = local.tags
+}
+
+module "ec2_targeted_capacity_reservation" {
+ source = "../../"
+
+ name = "${local.name}-targeted-capacity-reservation"
+
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = "t3.micro"
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = false
+
+ capacity_reservation_specification = {
+ capacity_reservation_target = {
+ capacity_reservation_id = aws_ec2_capacity_reservation.targeted.id
+ }
+ }
+
+ tags = local.tags
+}
+
+resource "aws_ec2_capacity_reservation" "open" {
+ instance_type = "t3.micro"
+ instance_platform = "Linux/UNIX"
+ availability_zone = "${local.region}a"
+ instance_count = 1
+ instance_match_criteria = "open"
+}
+
+resource "aws_ec2_capacity_reservation" "targeted" {
+ instance_type = "t3.micro"
+ instance_platform = "Linux/UNIX"
+ availability_zone = "${local.region}a"
+ instance_count = 1
+ instance_match_criteria = "targeted"
+}
+
+################################################################################
+# Supporting Resources
+################################################################################
+
+module "vpc" {
+ source = "terraform-aws-modules/vpc/aws"
+ version = "~> 3.0"
+
+ name = local.name
+ cidr = "10.99.0.0/18"
+
+ azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
+ public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"]
+ private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"]
+ database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"]
+
+ tags = local.tags
+}
+
+data "aws_ami" "amazon_linux" {
+ most_recent = true
+ owners = ["amazon"]
+
+ filter {
+ name = "name"
+ values = ["amzn-ami-hvm-*-x86_64-gp2"]
+ }
+}
+
+module "security_group" {
+ source = "terraform-aws-modules/security-group/aws"
+ version = "~> 4.0"
+
+ name = local.name
+ description = "Security group for example usage with EC2 instance"
+ vpc_id = module.vpc.vpc_id
+
+ ingress_cidr_blocks = ["0.0.0.0/0"]
+ ingress_rules = ["http-80-tcp", "all-icmp"]
+ egress_rules = ["all-all"]
+
+ tags = local.tags
+}
+
+resource "aws_placement_group" "web" {
+ name = local.name
+ strategy = "cluster"
+}
+
+resource "aws_kms_key" "this" {
+}
+
+resource "aws_network_interface" "this" {
+ subnet_id = element(module.vpc.private_subnets, 0)
+}
diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf
new file mode 100644
index 00000000..f99f56c1
--- /dev/null
+++ b/examples/complete/outputs.tf
@@ -0,0 +1,234 @@
+# EC2 Complete
+output "ec2_complete_id" {
+ description = "The ID of the instance"
+ value = module.ec2_complete.id
+}
+
+output "ec2_complete_arn" {
+ description = "The ARN of the instance"
+ value = module.ec2_complete.arn
+}
+
+output "ec2_complete_capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = module.ec2_complete.capacity_reservation_specification
+}
+
+output "ec2_complete_instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = module.ec2_complete.instance_state
+}
+
+output "ec2_complete_primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = module.ec2_complete.primary_network_interface_id
+}
+
+output "ec2_complete_private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_complete.private_dns
+}
+
+output "ec2_complete_public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_complete.public_dns
+}
+
+output "ec2_complete_public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = module.ec2_complete.public_ip
+}
+
+output "ec2_complete_tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = module.ec2_complete.tags_all
+}
+
+output "ec2_complete_iam_role_name" {
+ description = "The name of the IAM role"
+ value = module.ec2_complete.iam_role_name
+}
+
+output "ec2_complete_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = module.ec2_complete.iam_role_arn
+}
+
+output "ec2_complete_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = module.ec2_complete.iam_role_unique_id
+}
+
+output "ec2_complete_iam_instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = module.ec2_complete.iam_instance_profile_arn
+}
+
+output "ec2_complete_iam_instance_profile_id" {
+ description = "Instance profile's ID"
+ value = module.ec2_complete.iam_instance_profile_id
+}
+
+output "ec2_complete_iam_instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = module.ec2_complete.iam_instance_profile_unique
+}
+
+# EC2 T2 Unlimited
+output "ec2_t2_unlimited_id" {
+ description = "The ID of the instance"
+ value = module.ec2_t2_unlimited.id
+}
+
+output "ec2_t2_unlimited_arn" {
+ description = "The ARN of the instance"
+ value = module.ec2_t2_unlimited.arn
+}
+
+output "ec2_t2_unlimited_capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = module.ec2_t2_unlimited.capacity_reservation_specification
+}
+
+output "ec2_t2_unlimited_instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = module.ec2_t2_unlimited.instance_state
+}
+
+output "ec2_t2_unlimited_primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = module.ec2_t2_unlimited.primary_network_interface_id
+}
+
+output "ec2_t2_unlimited_private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_t2_unlimited.private_dns
+}
+
+output "ec2_t2_unlimited_public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_t2_unlimited.public_dns
+}
+
+output "ec2_t2_unlimited_public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = module.ec2_t2_unlimited.public_ip
+}
+
+output "ec2_t2_unlimited_tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = module.ec2_t2_unlimited.tags_all
+}
+
+# EC2 T3 Unlimited
+output "ec2_t3_unlimited_id" {
+ description = "The ID of the instance"
+ value = module.ec2_t3_unlimited.id
+}
+
+output "ec2_t3_unlimited_arn" {
+ description = "The ARN of the instance"
+ value = module.ec2_t3_unlimited.arn
+}
+
+output "ec2_t3_unlimited_capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = module.ec2_t3_unlimited.capacity_reservation_specification
+}
+
+output "ec2_t3_unlimited_instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = module.ec2_t3_unlimited.instance_state
+}
+
+output "ec2_t3_unlimited_primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = module.ec2_t3_unlimited.primary_network_interface_id
+}
+
+output "ec2_t3_unlimited_private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_t3_unlimited.private_dns
+}
+
+output "ec2_t3_unlimited_public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_t3_unlimited.public_dns
+}
+
+output "ec2_t3_unlimited_public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = module.ec2_t3_unlimited.public_ip
+}
+
+output "ec2_t3_unlimited_tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = module.ec2_t3_unlimited.tags_all
+}
+
+# EC2 Multiple
+output "ec2_multiple" {
+ description = "The full output of the `ec2_module` module"
+ value = module.ec2_multiple
+}
+
+# EC2 Spot Instance
+output "ec2_spot_instance_id" {
+ description = "The ID of the instance"
+ value = module.ec2_spot_instance.id
+}
+
+output "ec2_spot_instance_arn" {
+ description = "The ARN of the instance"
+ value = module.ec2_spot_instance.arn
+}
+
+output "ec2_spot_instance_capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = module.ec2_spot_instance.capacity_reservation_specification
+}
+
+output "ec2_spot_instance_instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = module.ec2_spot_instance.instance_state
+}
+
+output "ec2_spot_instance_primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = module.ec2_spot_instance.primary_network_interface_id
+}
+
+output "ec2_spot_instance_private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_spot_instance.private_dns
+}
+
+output "ec2_spot_instance_public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2_spot_instance.public_dns
+}
+
+output "ec2_spot_instance_public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = module.ec2_spot_instance.public_ip
+}
+
+output "ec2_spot_instance_tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = module.ec2_spot_instance.tags_all
+}
+
+output "spot_bid_status" {
+ description = "The current bid status of the Spot Instance Request"
+ value = module.ec2_spot_instance.spot_bid_status
+}
+
+output "spot_request_state" {
+ description = "The current request state of the Spot Instance Request"
+ value = module.ec2_spot_instance.spot_request_state
+}
+
+output "spot_instance_id" {
+ description = "The Instance ID (if any) that is currently fulfilling the Spot Instance request"
+ value = module.ec2_spot_instance.spot_instance_id
+}
diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf
new file mode 100644
index 00000000..36060f73
--- /dev/null
+++ b/examples/complete/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 0.13.1"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.7"
+ }
+ }
+}
diff --git a/examples/volume-attachment/README.md b/examples/volume-attachment/README.md
new file mode 100644
index 00000000..af4ffc6e
--- /dev/null
+++ b/examples/volume-attachment/README.md
@@ -0,0 +1,66 @@
+# EC2 instance with EBS volume attachment
+
+Configuration in this directory creates EC2 instances, EBS volume and attach it together.
+
+This example outputs instance id and EBS volume id.
+
+## Usage
+
+To run this example you need to execute:
+
+```bash
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+Note that this example may create resources which can cost money. Run `terraform destroy` when you don't need these resources.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 0.13.1 |
+| [aws](#requirement\_aws) | >= 3.72 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 3.72 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [ec2](#module\_ec2) | ../../ | n/a |
+| [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 |
+| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_ebs_volume.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ebs_volume) | resource |
+| [aws_volume_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/volume_attachment) | resource |
+| [aws_ami.amazon_linux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source |
+
+## Inputs
+
+No inputs.
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [ec2\_arn](#output\_ec2\_arn) | The ARN of the instance |
+| [ec2\_capacity\_reservation\_specification](#output\_ec2\_capacity\_reservation\_specification) | Capacity reservation specification of the instance |
+| [ec2\_id](#output\_ec2\_id) | The ID of the instance |
+| [ec2\_instance\_state](#output\_ec2\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` |
+| [ec2\_primary\_network\_interface\_id](#output\_ec2\_primary\_network\_interface\_id) | The ID of the instance's primary network interface |
+| [ec2\_private\_dns](#output\_ec2\_private\_dns) | The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_public\_dns](#output\_ec2\_public\_dns) | The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC |
+| [ec2\_public\_ip](#output\_ec2\_public\_ip) | The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws\_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached |
+| [ec2\_tags\_all](#output\_ec2\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider default\_tags configuration block |
+
diff --git a/examples/volume-attachment/main.tf b/examples/volume-attachment/main.tf
new file mode 100644
index 00000000..526773fe
--- /dev/null
+++ b/examples/volume-attachment/main.tf
@@ -0,0 +1,89 @@
+provider "aws" {
+ region = local.region
+}
+
+locals {
+ availability_zone = "${local.region}a"
+ name = "example-ec2-volume-attachment"
+ region = "eu-west-1"
+ tags = {
+ Owner = "user"
+ Environment = "dev"
+ }
+}
+
+################################################################################
+# Supporting Resources
+################################################################################
+
+module "vpc" {
+ source = "terraform-aws-modules/vpc/aws"
+ version = "~> 3.0"
+
+ name = local.name
+ cidr = "10.99.0.0/18"
+
+ azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
+ public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"]
+ private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"]
+ database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"]
+
+ tags = local.tags
+}
+
+data "aws_ami" "amazon_linux" {
+ most_recent = true
+ owners = ["amazon"]
+
+ filter {
+ name = "name"
+ values = ["amzn-ami-hvm-*-x86_64-gp2"]
+ }
+}
+
+module "security_group" {
+ source = "terraform-aws-modules/security-group/aws"
+ version = "~> 4.0"
+
+ name = local.name
+ description = "Security group for example usage with EC2 instance"
+ vpc_id = module.vpc.vpc_id
+
+ ingress_cidr_blocks = ["0.0.0.0/0"]
+ ingress_rules = ["http-80-tcp", "all-icmp"]
+ egress_rules = ["all-all"]
+
+ tags = local.tags
+}
+
+################################################################################
+# EC2 Module
+################################################################################
+
+module "ec2" {
+ source = "../../"
+
+ name = local.name
+
+ ami = data.aws_ami.amazon_linux.id
+ instance_type = "c5.large"
+ availability_zone = local.availability_zone
+ subnet_id = element(module.vpc.private_subnets, 0)
+ vpc_security_group_ids = [module.security_group.security_group_id]
+ associate_public_ip_address = true
+
+ tags = local.tags
+}
+
+resource "aws_volume_attachment" "this" {
+ device_name = "/dev/sdh"
+ volume_id = aws_ebs_volume.this.id
+ instance_id = module.ec2.id
+}
+
+resource "aws_ebs_volume" "this" {
+ availability_zone = local.availability_zone
+ size = 1
+
+ tags = local.tags
+}
diff --git a/examples/volume-attachment/outputs.tf b/examples/volume-attachment/outputs.tf
new file mode 100644
index 00000000..d9304a30
--- /dev/null
+++ b/examples/volume-attachment/outputs.tf
@@ -0,0 +1,45 @@
+# EC2
+output "ec2_id" {
+ description = "The ID of the instance"
+ value = module.ec2.id
+}
+
+output "ec2_arn" {
+ description = "The ARN of the instance"
+ value = module.ec2.arn
+}
+
+output "ec2_capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = module.ec2.capacity_reservation_specification
+}
+
+output "ec2_instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = module.ec2.instance_state
+}
+
+output "ec2_primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = module.ec2.primary_network_interface_id
+}
+
+output "ec2_private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2.private_dns
+}
+
+output "ec2_public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = module.ec2.public_dns
+}
+
+output "ec2_public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = module.ec2.public_ip
+}
+
+output "ec2_tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = module.ec2.tags_all
+}
diff --git a/examples/volume-attachment/variables.tf b/examples/volume-attachment/variables.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/volume-attachment/versions.tf b/examples/volume-attachment/versions.tf
new file mode 100644
index 00000000..22e8d726
--- /dev/null
+++ b/examples/volume-attachment/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 0.13.1"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 3.72"
+ }
+ }
+}
diff --git a/main.tf b/main.tf
new file mode 100644
index 00000000..9a0d4a23
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,363 @@
+data "aws_partition" "current" {}
+
+locals {
+ create = var.create && var.putin_khuylo
+
+ is_t_instance_type = replace(var.instance_type, "/^t(2|3|3a){1}\\..*$/", "1") == "1" ? true : false
+}
+
+data "aws_ssm_parameter" "this" {
+ count = local.create ? 1 : 0
+
+ name = var.ami_ssm_parameter
+}
+
+################################################################################
+# Instance
+################################################################################
+
+resource "aws_instance" "this" {
+ count = local.create && !var.create_spot_instance ? 1 : 0
+
+ ami = try(coalesce(var.ami, data.aws_ssm_parameter.this[0].value), null)
+ instance_type = var.instance_type
+ cpu_core_count = var.cpu_core_count
+ cpu_threads_per_core = var.cpu_threads_per_core
+ hibernation = var.hibernation
+
+ user_data = var.user_data
+ user_data_base64 = var.user_data_base64
+ user_data_replace_on_change = var.user_data_replace_on_change
+
+ availability_zone = var.availability_zone
+ subnet_id = var.subnet_id
+ vpc_security_group_ids = var.vpc_security_group_ids
+
+ key_name = var.key_name
+ monitoring = var.monitoring
+ get_password_data = var.get_password_data
+ iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile
+
+ associate_public_ip_address = var.associate_public_ip_address
+ private_ip = var.private_ip
+ secondary_private_ips = var.secondary_private_ips
+ ipv6_address_count = var.ipv6_address_count
+ ipv6_addresses = var.ipv6_addresses
+
+ ebs_optimized = var.ebs_optimized
+
+ dynamic "capacity_reservation_specification" {
+ for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : []
+ content {
+ capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null)
+
+ dynamic "capacity_reservation_target" {
+ for_each = try([capacity_reservation_specification.value.capacity_reservation_target], [])
+ content {
+ capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null)
+ capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null)
+ }
+ }
+ }
+ }
+
+ dynamic "root_block_device" {
+ for_each = var.root_block_device
+ content {
+ delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null)
+ encrypted = lookup(root_block_device.value, "encrypted", null)
+ iops = lookup(root_block_device.value, "iops", null)
+ kms_key_id = lookup(root_block_device.value, "kms_key_id", null)
+ volume_size = lookup(root_block_device.value, "volume_size", null)
+ volume_type = lookup(root_block_device.value, "volume_type", null)
+ throughput = lookup(root_block_device.value, "throughput", null)
+ tags = lookup(root_block_device.value, "tags", null)
+ }
+ }
+
+ dynamic "ebs_block_device" {
+ for_each = var.ebs_block_device
+ content {
+ delete_on_termination = lookup(ebs_block_device.value, "delete_on_termination", null)
+ device_name = ebs_block_device.value.device_name
+ encrypted = lookup(ebs_block_device.value, "encrypted", null)
+ iops = lookup(ebs_block_device.value, "iops", null)
+ kms_key_id = lookup(ebs_block_device.value, "kms_key_id", null)
+ snapshot_id = lookup(ebs_block_device.value, "snapshot_id", null)
+ volume_size = lookup(ebs_block_device.value, "volume_size", null)
+ volume_type = lookup(ebs_block_device.value, "volume_type", null)
+ throughput = lookup(ebs_block_device.value, "throughput", null)
+ }
+ }
+
+ dynamic "ephemeral_block_device" {
+ for_each = var.ephemeral_block_device
+ content {
+ device_name = ephemeral_block_device.value.device_name
+ no_device = lookup(ephemeral_block_device.value, "no_device", null)
+ virtual_name = lookup(ephemeral_block_device.value, "virtual_name", null)
+ }
+ }
+
+ dynamic "metadata_options" {
+ for_each = var.metadata_options != null ? [var.metadata_options] : []
+ content {
+ http_endpoint = lookup(metadata_options.value, "http_endpoint", "enabled")
+ http_tokens = lookup(metadata_options.value, "http_tokens", "optional")
+ http_put_response_hop_limit = lookup(metadata_options.value, "http_put_response_hop_limit", "1")
+ instance_metadata_tags = lookup(metadata_options.value, "instance_metadata_tags", null)
+ }
+ }
+
+ dynamic "network_interface" {
+ for_each = var.network_interface
+ content {
+ device_index = network_interface.value.device_index
+ network_interface_id = lookup(network_interface.value, "network_interface_id", null)
+ delete_on_termination = lookup(network_interface.value, "delete_on_termination", false)
+ }
+ }
+
+ dynamic "launch_template" {
+ for_each = var.launch_template != null ? [var.launch_template] : []
+ content {
+ id = lookup(var.launch_template, "id", null)
+ name = lookup(var.launch_template, "name", null)
+ version = lookup(var.launch_template, "version", null)
+ }
+ }
+
+ enclave_options {
+ enabled = var.enclave_options_enabled
+ }
+
+ source_dest_check = length(var.network_interface) > 0 ? null : var.source_dest_check
+ disable_api_termination = var.disable_api_termination
+ disable_api_stop = var.disable_api_stop
+ instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior
+ placement_group = var.placement_group
+ tenancy = var.tenancy
+ host_id = var.host_id
+
+ credit_specification {
+ cpu_credits = local.is_t_instance_type ? var.cpu_credits : null
+ }
+
+ timeouts {
+ create = lookup(var.timeouts, "create", null)
+ update = lookup(var.timeouts, "update", null)
+ delete = lookup(var.timeouts, "delete", null)
+ }
+
+ tags = merge({ "Name" = var.name }, var.tags)
+ volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null
+}
+
+################################################################################
+# Spot Instance
+################################################################################
+
+resource "aws_spot_instance_request" "this" {
+ count = local.create && var.create_spot_instance ? 1 : 0
+
+ ami = try(coalesce(var.ami, data.aws_ssm_parameter.this[0].value), null)
+ instance_type = var.instance_type
+ cpu_core_count = var.cpu_core_count
+ cpu_threads_per_core = var.cpu_threads_per_core
+ hibernation = var.hibernation
+
+ user_data = var.user_data
+ user_data_base64 = var.user_data_base64
+ user_data_replace_on_change = var.user_data_replace_on_change
+
+ availability_zone = var.availability_zone
+ subnet_id = var.subnet_id
+ vpc_security_group_ids = var.vpc_security_group_ids
+
+ key_name = var.key_name
+ monitoring = var.monitoring
+ get_password_data = var.get_password_data
+ iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile
+
+ associate_public_ip_address = var.associate_public_ip_address
+ private_ip = var.private_ip
+ secondary_private_ips = var.secondary_private_ips
+ ipv6_address_count = var.ipv6_address_count
+ ipv6_addresses = var.ipv6_addresses
+
+ ebs_optimized = var.ebs_optimized
+
+ # Spot request specific attributes
+ spot_price = var.spot_price
+ wait_for_fulfillment = var.spot_wait_for_fulfillment
+ spot_type = var.spot_type
+ launch_group = var.spot_launch_group
+ block_duration_minutes = var.spot_block_duration_minutes
+ instance_interruption_behavior = var.spot_instance_interruption_behavior
+ valid_until = var.spot_valid_until
+ valid_from = var.spot_valid_from
+ # End spot request specific attributes
+
+ dynamic "capacity_reservation_specification" {
+ for_each = length(var.capacity_reservation_specification) > 0 ? [var.capacity_reservation_specification] : []
+ content {
+ capacity_reservation_preference = try(capacity_reservation_specification.value.capacity_reservation_preference, null)
+
+ dynamic "capacity_reservation_target" {
+ for_each = try([capacity_reservation_specification.value.capacity_reservation_target], [])
+ content {
+ capacity_reservation_id = try(capacity_reservation_target.value.capacity_reservation_id, null)
+ capacity_reservation_resource_group_arn = try(capacity_reservation_target.value.capacity_reservation_resource_group_arn, null)
+ }
+ }
+ }
+ }
+
+ dynamic "root_block_device" {
+ for_each = var.root_block_device
+ content {
+ delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null)
+ encrypted = lookup(root_block_device.value, "encrypted", null)
+ iops = lookup(root_block_device.value, "iops", null)
+ kms_key_id = lookup(root_block_device.value, "kms_key_id", null)
+ volume_size = lookup(root_block_device.value, "volume_size", null)
+ volume_type = lookup(root_block_device.value, "volume_type", null)
+ throughput = lookup(root_block_device.value, "throughput", null)
+ tags = lookup(root_block_device.value, "tags", null)
+ }
+ }
+
+ dynamic "ebs_block_device" {
+ for_each = var.ebs_block_device
+ content {
+ delete_on_termination = lookup(ebs_block_device.value, "delete_on_termination", null)
+ device_name = ebs_block_device.value.device_name
+ encrypted = lookup(ebs_block_device.value, "encrypted", null)
+ iops = lookup(ebs_block_device.value, "iops", null)
+ kms_key_id = lookup(ebs_block_device.value, "kms_key_id", null)
+ snapshot_id = lookup(ebs_block_device.value, "snapshot_id", null)
+ volume_size = lookup(ebs_block_device.value, "volume_size", null)
+ volume_type = lookup(ebs_block_device.value, "volume_type", null)
+ throughput = lookup(ebs_block_device.value, "throughput", null)
+ }
+ }
+
+ dynamic "ephemeral_block_device" {
+ for_each = var.ephemeral_block_device
+ content {
+ device_name = ephemeral_block_device.value.device_name
+ no_device = lookup(ephemeral_block_device.value, "no_device", null)
+ virtual_name = lookup(ephemeral_block_device.value, "virtual_name", null)
+ }
+ }
+
+ dynamic "metadata_options" {
+ for_each = var.metadata_options != null ? [var.metadata_options] : []
+ content {
+ http_endpoint = lookup(metadata_options.value, "http_endpoint", "enabled")
+ http_tokens = lookup(metadata_options.value, "http_tokens", "optional")
+ http_put_response_hop_limit = lookup(metadata_options.value, "http_put_response_hop_limit", "1")
+ }
+ }
+
+ dynamic "network_interface" {
+ for_each = var.network_interface
+ content {
+ device_index = network_interface.value.device_index
+ network_interface_id = lookup(network_interface.value, "network_interface_id", null)
+ delete_on_termination = lookup(network_interface.value, "delete_on_termination", false)
+ }
+ }
+
+ dynamic "launch_template" {
+ for_each = var.launch_template != null ? [var.launch_template] : []
+ content {
+ id = lookup(var.launch_template, "id", null)
+ name = lookup(var.launch_template, "name", null)
+ version = lookup(var.launch_template, "version", null)
+ }
+ }
+
+ enclave_options {
+ enabled = var.enclave_options_enabled
+ }
+
+ source_dest_check = length(var.network_interface) > 0 ? null : var.source_dest_check
+ disable_api_termination = var.disable_api_termination
+ instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior
+ placement_group = var.placement_group
+ tenancy = var.tenancy
+ host_id = var.host_id
+
+ credit_specification {
+ cpu_credits = local.is_t_instance_type ? var.cpu_credits : null
+ }
+
+ timeouts {
+ create = lookup(var.timeouts, "create", null)
+ delete = lookup(var.timeouts, "delete", null)
+ }
+
+ tags = merge({ "Name" = var.name }, var.tags)
+ volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null
+}
+
+################################################################################
+# IAM Role / Instance Profile
+################################################################################
+
+locals {
+ iam_role_name = try(coalesce(var.iam_role_name, var.name), "")
+}
+
+data "aws_iam_policy_document" "assume_role_policy" {
+ count = var.create && var.create_iam_instance_profile ? 1 : 0
+
+ statement {
+ sid = "EC2AssumeRole"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["ec2.${data.aws_partition.current.dns_suffix}"]
+ }
+ }
+}
+
+resource "aws_iam_role" "this" {
+ count = var.create && var.create_iam_instance_profile ? 1 : 0
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+ description = var.iam_role_description
+
+ assume_role_policy = data.aws_iam_policy_document.assume_role_policy[0].json
+ permissions_boundary = var.iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.iam_role_tags)
+}
+
+resource "aws_iam_role_policy_attachment" "this" {
+ for_each = { for k, v in var.iam_role_policies : k => v if var.create && var.create_iam_instance_profile }
+
+ policy_arn = each.value
+ role = aws_iam_role.this[0].name
+}
+
+resource "aws_iam_instance_profile" "this" {
+ count = var.create && var.create_iam_instance_profile ? 1 : 0
+
+ role = aws_iam_role.this[0].name
+
+ name = var.iam_role_use_name_prefix ? null : local.iam_role_name
+ name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
+ path = var.iam_role_path
+
+ tags = merge(var.tags, var.iam_role_tags)
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 00000000..45f42545
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1,113 @@
+output "id" {
+ description = "The ID of the instance"
+ value = try(aws_instance.this[0].id, aws_spot_instance_request.this[0].id, "")
+}
+
+output "arn" {
+ description = "The ARN of the instance"
+ value = try(aws_instance.this[0].arn, aws_spot_instance_request.this[0].arn, "")
+}
+
+output "capacity_reservation_specification" {
+ description = "Capacity reservation specification of the instance"
+ value = try(aws_instance.this[0].capacity_reservation_specification, aws_spot_instance_request.this[0].capacity_reservation_specification, "")
+}
+
+output "instance_state" {
+ description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`"
+ value = try(aws_instance.this[0].instance_state, aws_spot_instance_request.this[0].instance_state, "")
+}
+
+output "outpost_arn" {
+ description = "The ARN of the Outpost the instance is assigned to"
+ value = try(aws_instance.this[0].outpost_arn, aws_spot_instance_request.this[0].outpost_arn, "")
+}
+
+output "password_data" {
+ description = "Base-64 encoded encrypted password data for the instance. Useful for getting the administrator password for instances running Microsoft Windows. This attribute is only exported if `get_password_data` is true"
+ value = try(aws_instance.this[0].password_data, aws_spot_instance_request.this[0].password_data, "")
+}
+
+output "primary_network_interface_id" {
+ description = "The ID of the instance's primary network interface"
+ value = try(aws_instance.this[0].primary_network_interface_id, aws_spot_instance_request.this[0].primary_network_interface_id, "")
+}
+
+output "private_dns" {
+ description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC"
+ value = try(aws_instance.this[0].private_dns, aws_spot_instance_request.this[0].private_dns, "")
+}
+
+output "public_dns" {
+ description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC"
+ value = try(aws_instance.this[0].public_dns, aws_spot_instance_request.this[0].public_dns, "")
+}
+
+output "public_ip" {
+ description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
+ value = try(aws_instance.this[0].public_ip, aws_spot_instance_request.this[0].public_ip, "")
+}
+
+output "private_ip" {
+ description = "The private IP address assigned to the instance."
+ value = try(aws_instance.this[0].private_ip, aws_spot_instance_request.this[0].private_ip, "")
+}
+
+output "ipv6_addresses" {
+ description = "The IPv6 address assigned to the instance, if applicable."
+ value = try(aws_instance.this[0].ipv6_addresses, [])
+}
+
+output "tags_all" {
+ description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block"
+ value = try(aws_instance.this[0].tags_all, aws_spot_instance_request.this[0].tags_all, {})
+}
+
+output "spot_bid_status" {
+ description = "The current bid status of the Spot Instance Request"
+ value = try(aws_spot_instance_request.this[0].spot_bid_status, "")
+}
+
+output "spot_request_state" {
+ description = "The current request state of the Spot Instance Request"
+ value = try(aws_spot_instance_request.this[0].spot_request_state, "")
+}
+
+output "spot_instance_id" {
+ description = "The Instance ID (if any) that is currently fulfilling the Spot Instance request"
+ value = try(aws_spot_instance_request.this[0].spot_instance_id, "")
+}
+
+################################################################################
+# IAM Role / Instance Profile
+################################################################################
+
+output "iam_role_name" {
+ description = "The name of the IAM role"
+ value = try(aws_iam_role.this[0].name, null)
+}
+
+output "iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.this[0].arn, null)
+}
+
+output "iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.this[0].unique_id, null)
+}
+
+output "iam_instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = try(aws_iam_instance_profile.this[0].arn, null)
+}
+
+output "iam_instance_profile_id" {
+ description = "Instance profile's ID"
+ value = try(aws_iam_instance_profile.this[0].id, null)
+}
+
+output "iam_instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = try(aws_iam_instance_profile.this[0].unique_id, null)
+}
diff --git a/variables.tf b/variables.tf
new file mode 100644
index 00000000..584a62d6
--- /dev/null
+++ b/variables.tf
@@ -0,0 +1,376 @@
+variable "create" {
+ description = "Whether to create an instance"
+ type = bool
+ default = true
+}
+
+variable "name" {
+ description = "Name to be used on EC2 instance created"
+ type = string
+ default = ""
+}
+
+variable "ami_ssm_parameter" {
+ description = "SSM parameter name for the AMI ID. For Amazon Linux AMI SSM parameters see [reference](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-public-parameters-ami.html)"
+ type = string
+ default = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
+}
+
+variable "ami" {
+ description = "ID of AMI to use for the instance"
+ type = string
+ default = null
+}
+
+variable "associate_public_ip_address" {
+ description = "Whether to associate a public IP address with an instance in a VPC"
+ type = bool
+ default = null
+}
+
+variable "availability_zone" {
+ description = "AZ to start the instance in"
+ type = string
+ default = null
+}
+
+variable "capacity_reservation_specification" {
+ description = "Describes an instance's Capacity Reservation targeting option"
+ type = any
+ default = {}
+}
+
+variable "cpu_credits" {
+ description = "The credit option for CPU usage (unlimited or standard)"
+ type = string
+ default = null
+}
+
+variable "disable_api_termination" {
+ description = "If true, enables EC2 Instance Termination Protection"
+ type = bool
+ default = null
+}
+
+variable "ebs_block_device" {
+ description = "Additional EBS block devices to attach to the instance"
+ type = list(map(string))
+ default = []
+}
+
+variable "ebs_optimized" {
+ description = "If true, the launched EC2 instance will be EBS-optimized"
+ type = bool
+ default = null
+}
+
+variable "enclave_options_enabled" {
+ description = "Whether Nitro Enclaves will be enabled on the instance. Defaults to `false`"
+ type = bool
+ default = null
+}
+
+variable "ephemeral_block_device" {
+ description = "Customize Ephemeral (also known as Instance Store) volumes on the instance"
+ type = list(map(string))
+ default = []
+}
+
+variable "get_password_data" {
+ description = "If true, wait for password data to become available and retrieve it."
+ type = bool
+ default = null
+}
+
+variable "hibernation" {
+ description = "If true, the launched EC2 instance will support hibernation"
+ type = bool
+ default = null
+}
+
+variable "host_id" {
+ description = "ID of a dedicated host that the instance will be assigned to. Use when an instance is to be launched on a specific dedicated host"
+ type = string
+ default = null
+}
+
+variable "iam_instance_profile" {
+ description = "IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile"
+ type = string
+ default = null
+}
+
+variable "instance_initiated_shutdown_behavior" {
+ description = "Shutdown behavior for the instance. Amazon defaults this to stop for EBS-backed instances and terminate for instance-store instances. Cannot be set on instance-store instance" # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior
+ type = string
+ default = null
+}
+
+variable "instance_type" {
+ description = "The type of instance to start"
+ type = string
+ default = "t3.micro"
+}
+
+variable "ipv6_address_count" {
+ description = "A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet"
+ type = number
+ default = null
+}
+
+variable "ipv6_addresses" {
+ description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
+ type = list(string)
+ default = null
+}
+
+variable "key_name" {
+ description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
+ type = string
+ default = null
+}
+
+variable "launch_template" {
+ description = "Specifies a Launch Template to configure the instance. Parameters configured on this resource will override the corresponding parameters in the Launch Template"
+ type = map(string)
+ default = null
+}
+
+variable "metadata_options" {
+ description = "Customize the metadata options of the instance"
+ type = map(string)
+ default = {}
+}
+
+variable "monitoring" {
+ description = "If true, the launched EC2 instance will have detailed monitoring enabled"
+ type = bool
+ default = false
+}
+
+variable "network_interface" {
+ description = "Customize network interfaces to be attached at instance boot time"
+ type = list(map(string))
+ default = []
+}
+
+variable "placement_group" {
+ description = "The Placement Group to start the instance in"
+ type = string
+ default = null
+}
+
+variable "private_ip" {
+ description = "Private IP address to associate with the instance in a VPC"
+ type = string
+ default = null
+}
+
+variable "root_block_device" {
+ description = "Customize details about the root block device of the instance. See Block Devices below for details"
+ type = list(any)
+ default = []
+}
+
+variable "secondary_private_ips" {
+ description = "A list of secondary private IPv4 addresses to assign to the instance's primary network interface (eth0) in a VPC. Can only be assigned to the primary network interface (eth0) attached at instance creation, not a pre-existing network interface i.e. referenced in a `network_interface block`"
+ type = list(string)
+ default = null
+}
+
+variable "source_dest_check" {
+ description = "Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs."
+ type = bool
+ default = true
+}
+
+variable "subnet_id" {
+ description = "The VPC Subnet ID to launch in"
+ type = string
+ default = null
+}
+
+variable "tags" {
+ description = "A mapping of tags to assign to the resource"
+ type = map(string)
+ default = {}
+}
+
+variable "tenancy" {
+ description = "The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host."
+ type = string
+ default = null
+}
+
+variable "user_data" {
+ description = "The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user_data_base64 instead."
+ type = string
+ default = null
+}
+
+variable "user_data_base64" {
+ description = "Can be used instead of user_data to pass base64-encoded binary data directly. Use this instead of user_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption."
+ type = string
+ default = null
+}
+
+variable "user_data_replace_on_change" {
+ description = "When used in combination with user_data or user_data_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set."
+ type = bool
+ default = false
+}
+
+variable "volume_tags" {
+ description = "A mapping of tags to assign to the devices created by the instance at launch time"
+ type = map(string)
+ default = {}
+}
+
+variable "enable_volume_tags" {
+ description = "Whether to enable volume tags (if enabled it conflicts with root_block_device tags)"
+ type = bool
+ default = true
+}
+
+variable "vpc_security_group_ids" {
+ description = "A list of security group IDs to associate with"
+ type = list(string)
+ default = null
+}
+
+variable "timeouts" {
+ description = "Define maximum timeout for creating, updating, and deleting EC2 instance resources"
+ type = map(string)
+ default = {}
+}
+
+variable "cpu_core_count" {
+ description = "Sets the number of CPU cores for an instance." # This option is only supported on creation of instance type that support CPU Options https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-optimize-cpu.html#cpu-options-supported-instances-values
+ type = number
+ default = null
+}
+
+variable "cpu_threads_per_core" {
+ description = "Sets the number of CPU threads per core for an instance (has no effect unless cpu_core_count is also set)."
+ type = number
+ default = null
+}
+
+# Spot instance request
+variable "create_spot_instance" {
+ description = "Depicts if the instance is a spot instance"
+ type = bool
+ default = false
+}
+
+variable "spot_price" {
+ description = "The maximum price to request on the spot market. Defaults to on-demand price"
+ type = string
+ default = null
+}
+
+variable "spot_wait_for_fulfillment" {
+ description = "If set, Terraform will wait for the Spot Request to be fulfilled, and will throw an error if the timeout of 10m is reached"
+ type = bool
+ default = null
+}
+
+variable "spot_type" {
+ description = "If set to one-time, after the instance is terminated, the spot request will be closed. Default `persistent`"
+ type = string
+ default = null
+}
+
+variable "spot_launch_group" {
+ description = "A launch group is a group of spot instances that launch together and terminate together. If left empty instances are launched and terminated individually"
+ type = string
+ default = null
+}
+
+variable "spot_block_duration_minutes" {
+ description = "The required duration for the Spot instances, in minutes. This value must be a multiple of 60 (60, 120, 180, 240, 300, or 360)"
+ type = number
+ default = null
+}
+
+variable "spot_instance_interruption_behavior" {
+ description = "Indicates Spot instance behavior when it is interrupted. Valid values are `terminate`, `stop`, or `hibernate`"
+ type = string
+ default = null
+}
+
+variable "spot_valid_until" {
+ description = "The end date and time of the request, in UTC RFC3339 format(for example, YYYY-MM-DDTHH:MM:SSZ)"
+ type = string
+ default = null
+}
+
+variable "spot_valid_from" {
+ description = "The start date and time of the request, in UTC RFC3339 format(for example, YYYY-MM-DDTHH:MM:SSZ)"
+ type = string
+ default = null
+}
+
+variable "disable_api_stop" {
+ description = "If true, enables EC2 Instance Stop Protection."
+ type = bool
+ default = null
+
+}
+variable "putin_khuylo" {
+ description = "Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo!"
+ type = bool
+ default = true
+}
+
+################################################################################
+# IAM Role / Instance Profile
+################################################################################
+
+variable "create_iam_instance_profile" {
+ description = "Determines whether an IAM instance profile is created or to use an existing IAM instance profile"
+ type = bool
+ default = false
+}
+
+variable "iam_role_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+variable "iam_role_use_name_prefix" {
+ description = "Determines whether the IAM role name (`iam_role_name` or `name`) is used as a prefix"
+ type = bool
+ default = true
+}
+
+variable "iam_role_path" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = null
+}
+
+variable "iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "iam_role_policies" {
+ description = "Policies attached to the IAM role"
+ type = map(string)
+ default = {}
+}
+
+variable "iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role/profile created"
+ type = map(string)
+ default = {}
+}
diff --git a/versions.tf b/versions.tf
new file mode 100644
index 00000000..0836352d
--- /dev/null
+++ b/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 0.13.1"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.20.0"
+ }
+ }
+}
diff --git a/wrappers/README.md b/wrappers/README.md
new file mode 100644
index 00000000..dbb2afd1
--- /dev/null
+++ b/wrappers/README.md
@@ -0,0 +1,100 @@
+# Wrapper for the root module
+
+The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt).
+
+You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module.
+
+This wrapper does not implement any extra functionality.
+
+## Usage with Terragrunt
+
+`terragrunt.hcl`:
+
+```hcl
+terraform {
+ source = "tfr:///terraform-aws-modules/ec2-instance/aws//wrappers"
+ # Alternative source:
+ # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ec2-instance.git//wrappers?ref=master"
+}
+
+inputs = {
+ defaults = { # Default values
+ create = true
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+ }
+
+ items = {
+ my-item = {
+ # omitted... can be any argument supported by the module
+ }
+ my-second-item = {
+ # omitted... can be any argument supported by the module
+ }
+ # omitted...
+ }
+}
+```
+
+## Usage with Terraform
+
+```hcl
+module "wrapper" {
+ source = "terraform-aws-modules/ec2-instance/aws//wrappers"
+
+ defaults = { # Default values
+ create = true
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+ }
+
+ items = {
+ my-item = {
+ # omitted... can be any argument supported by the module
+ }
+ my-second-item = {
+ # omitted... can be any argument supported by the module
+ }
+ # omitted...
+ }
+}
+```
+
+## Example: Manage multiple S3 buckets in one Terragrunt layer
+
+`eu-west-1/s3-buckets/terragrunt.hcl`:
+
+```hcl
+terraform {
+ source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers"
+ # Alternative source:
+ # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master"
+}
+
+inputs = {
+ defaults = {
+ force_destroy = true
+
+ attach_elb_log_delivery_policy = true
+ attach_lb_log_delivery_policy = true
+ attach_deny_insecure_transport_policy = true
+ attach_require_latest_tls_policy = true
+ }
+
+ items = {
+ bucket1 = {
+ bucket = "my-random-bucket-1"
+ }
+ bucket2 = {
+ bucket = "my-random-bucket-2"
+ tags = {
+ Secure = "probably"
+ }
+ }
+ }
+}
+```
diff --git a/wrappers/main.tf b/wrappers/main.tf
new file mode 100644
index 00000000..4b9237bf
--- /dev/null
+++ b/wrappers/main.tf
@@ -0,0 +1,68 @@
+module "wrapper" {
+ source = "../"
+
+ for_each = var.items
+
+ create = try(each.value.create, var.defaults.create, true)
+ name = try(each.value.name, var.defaults.name, "")
+ ami_ssm_parameter = try(each.value.ami_ssm_parameter, var.defaults.ami_ssm_parameter, "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2")
+ ami = try(each.value.ami, var.defaults.ami, null)
+ associate_public_ip_address = try(each.value.associate_public_ip_address, var.defaults.associate_public_ip_address, null)
+ availability_zone = try(each.value.availability_zone, var.defaults.availability_zone, null)
+ capacity_reservation_specification = try(each.value.capacity_reservation_specification, var.defaults.capacity_reservation_specification, {})
+ cpu_credits = try(each.value.cpu_credits, var.defaults.cpu_credits, null)
+ disable_api_termination = try(each.value.disable_api_termination, var.defaults.disable_api_termination, null)
+ ebs_block_device = try(each.value.ebs_block_device, var.defaults.ebs_block_device, [])
+ ebs_optimized = try(each.value.ebs_optimized, var.defaults.ebs_optimized, null)
+ enclave_options_enabled = try(each.value.enclave_options_enabled, var.defaults.enclave_options_enabled, null)
+ ephemeral_block_device = try(each.value.ephemeral_block_device, var.defaults.ephemeral_block_device, [])
+ get_password_data = try(each.value.get_password_data, var.defaults.get_password_data, null)
+ hibernation = try(each.value.hibernation, var.defaults.hibernation, null)
+ host_id = try(each.value.host_id, var.defaults.host_id, null)
+ iam_instance_profile = try(each.value.iam_instance_profile, var.defaults.iam_instance_profile, null)
+ instance_initiated_shutdown_behavior = try(each.value.instance_initiated_shutdown_behavior, var.defaults.instance_initiated_shutdown_behavior, null)
+ instance_type = try(each.value.instance_type, var.defaults.instance_type, "t3.micro")
+ ipv6_address_count = try(each.value.ipv6_address_count, var.defaults.ipv6_address_count, null)
+ ipv6_addresses = try(each.value.ipv6_addresses, var.defaults.ipv6_addresses, null)
+ key_name = try(each.value.key_name, var.defaults.key_name, null)
+ launch_template = try(each.value.launch_template, var.defaults.launch_template, null)
+ metadata_options = try(each.value.metadata_options, var.defaults.metadata_options, {})
+ monitoring = try(each.value.monitoring, var.defaults.monitoring, false)
+ network_interface = try(each.value.network_interface, var.defaults.network_interface, [])
+ placement_group = try(each.value.placement_group, var.defaults.placement_group, null)
+ private_ip = try(each.value.private_ip, var.defaults.private_ip, null)
+ root_block_device = try(each.value.root_block_device, var.defaults.root_block_device, [])
+ secondary_private_ips = try(each.value.secondary_private_ips, var.defaults.secondary_private_ips, null)
+ source_dest_check = try(each.value.source_dest_check, var.defaults.source_dest_check, true)
+ subnet_id = try(each.value.subnet_id, var.defaults.subnet_id, null)
+ tags = try(each.value.tags, var.defaults.tags, {})
+ tenancy = try(each.value.tenancy, var.defaults.tenancy, null)
+ user_data = try(each.value.user_data, var.defaults.user_data, null)
+ user_data_base64 = try(each.value.user_data_base64, var.defaults.user_data_base64, null)
+ user_data_replace_on_change = try(each.value.user_data_replace_on_change, var.defaults.user_data_replace_on_change, false)
+ volume_tags = try(each.value.volume_tags, var.defaults.volume_tags, {})
+ enable_volume_tags = try(each.value.enable_volume_tags, var.defaults.enable_volume_tags, true)
+ vpc_security_group_ids = try(each.value.vpc_security_group_ids, var.defaults.vpc_security_group_ids, null)
+ timeouts = try(each.value.timeouts, var.defaults.timeouts, {})
+ cpu_core_count = try(each.value.cpu_core_count, var.defaults.cpu_core_count, null)
+ cpu_threads_per_core = try(each.value.cpu_threads_per_core, var.defaults.cpu_threads_per_core, null)
+ create_spot_instance = try(each.value.create_spot_instance, var.defaults.create_spot_instance, false)
+ spot_price = try(each.value.spot_price, var.defaults.spot_price, null)
+ spot_wait_for_fulfillment = try(each.value.spot_wait_for_fulfillment, var.defaults.spot_wait_for_fulfillment, null)
+ spot_type = try(each.value.spot_type, var.defaults.spot_type, null)
+ spot_launch_group = try(each.value.spot_launch_group, var.defaults.spot_launch_group, null)
+ spot_block_duration_minutes = try(each.value.spot_block_duration_minutes, var.defaults.spot_block_duration_minutes, null)
+ spot_instance_interruption_behavior = try(each.value.spot_instance_interruption_behavior, var.defaults.spot_instance_interruption_behavior, null)
+ spot_valid_until = try(each.value.spot_valid_until, var.defaults.spot_valid_until, null)
+ spot_valid_from = try(each.value.spot_valid_from, var.defaults.spot_valid_from, null)
+ disable_api_stop = try(each.value.disable_api_stop, var.defaults.disable_api_stop, null)
+ putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true)
+ create_iam_instance_profile = try(each.value.create_iam_instance_profile, var.defaults.create_iam_instance_profile, false)
+ iam_role_name = try(each.value.iam_role_name, var.defaults.iam_role_name, null)
+ iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true)
+ iam_role_path = try(each.value.iam_role_path, var.defaults.iam_role_path, null)
+ iam_role_description = try(each.value.iam_role_description, var.defaults.iam_role_description, null)
+ iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.defaults.iam_role_permissions_boundary, null)
+ iam_role_policies = try(each.value.iam_role_policies, var.defaults.iam_role_policies, {})
+ iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {})
+}
diff --git a/wrappers/outputs.tf b/wrappers/outputs.tf
new file mode 100644
index 00000000..5da7c09b
--- /dev/null
+++ b/wrappers/outputs.tf
@@ -0,0 +1,5 @@
+output "wrapper" {
+ description = "Map of outputs of a wrapper."
+ value = module.wrapper
+ # sensitive = false # No sensitive module output found
+}
diff --git a/wrappers/variables.tf b/wrappers/variables.tf
new file mode 100644
index 00000000..a6ea0962
--- /dev/null
+++ b/wrappers/variables.tf
@@ -0,0 +1,11 @@
+variable "defaults" {
+ description = "Map of default values which will be used for each item."
+ type = any
+ default = {}
+}
+
+variable "items" {
+ description = "Maps of items to create a wrapper from. Values are passed through to the module."
+ type = any
+ default = {}
+}
diff --git a/wrappers/versions.tf b/wrappers/versions.tf
new file mode 100644
index 00000000..51cad108
--- /dev/null
+++ b/wrappers/versions.tf
@@ -0,0 +1,3 @@
+terraform {
+ required_version = ">= 0.13.1"
+}