Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added support for tfupdate to update version constraints in Terraform configurations #342

Merged
merged 7 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,13 @@
files: \.tf$
exclude: \.terraform\/.*$
require_serial: true

- id: tfupdate
name: tfupdate
description: Runs tfupdate on Terraform templates.
language: script
entry: hooks/tfupdate.sh
args:
- --args=terraform
files: \.tf$
require_serial: true
15 changes: 14 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ARG TERRAGRUNT_VERSION=${TERRAGRUNT_VERSION:-false}
ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false}
ARG TFLINT_VERSION=${TFLINT_VERSION:-false}
ARG TFSEC_VERSION=${TFSEC_VERSION:-false}
ARG TFUPDATE_VERSION=${TFUPDATE_VERSION:-false}


# Tricky thing to install all tools by set only one arg.
Expand All @@ -47,7 +48,8 @@ RUN if [ "$INSTALL_ALL" != "false" ]; then \
echo "export TERRAGRUNT_VERSION=latest" >> /.env && \
echo "export TERRASCAN_VERSION=latest" >> /.env && \
echo "export TFLINT_VERSION=latest" >> /.env && \
echo "export TFSEC_VERSION=latest" >> /.env \
echo "export TFSEC_VERSION=latest" >> /.env && \
echo "export TFUPDATE_VERSION=latest" >> /.env \
; else \
touch /.env \
; fi
Expand Down Expand Up @@ -126,6 +128,16 @@ RUN . /.env && \
) && chmod +x tfsec \
; fi

# TFUpdate
RUN . /.env && \
if [ "$TFUPDATE_VERSION" != "false" ]; then \
( \
TFUPDATE_RELEASES="https://api.github.com/repos/minamijoyo/tfupdate/releases" && \
[ "$TFUPDATE_VERSION" = "latest" ] && curl -L "$(curl -s ${TFUPDATE_RELEASES}/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > tfupdate.tgz \
|| curl -L "$(curl -s ${TFUPDATE_RELEASES} | grep -o -E -m 1 "https://.+?${TFUPDATE_VERSION}_linux_amd64.tar.gz")" > tfupdate.tgz \
) && tar -xzf tfupdate.tgz tfupdate && rm tfupdate.tgz \
; fi

# Checking binaries versions and write it to debug file
RUN . /.env && \
F=tools_versions_info && \
Expand All @@ -138,6 +150,7 @@ RUN . /.env && \
(if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \
(if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \
(if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \
(if [ "$TFUPDATE_VERSION" != "false" ]; then echo "tfupdate $(./tfupdate --version)" >> $F; else echo "tfupdate SKIPPED" >> $F ; fi) && \
echo -e "\n\n" && cat $F && echo -e "\n\n"


Expand Down
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
* [terraform_tfsec](#terraform_tfsec)
* [terraform_validate](#terraform_validate)
* [terrascan](#terrascan)
* [tfupdate](#tfupdate)
* [Authors](#authors)
* [License](#license)
* [Additional terms of use for users from Russia and Belarus](#additional-terms-of-use-for-users-from-russia-and-belarus)
Expand All @@ -65,6 +66,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook.
* [`infracost`](https://github.com/infracost/infracost) required for `infracost_breakdown` hook.
* [`jq`](https://github.com/stedolan/jq) required for `infracost_breakdown` hook.
* [`tfupdate`](https://github.com/minamijoyo/tfupdate) required for `tfupdate` hook.

<details><summary><b>Docker</b></summary><br>

Expand Down Expand Up @@ -101,6 +103,7 @@ docker build -t pre-commit-terraform \
--build-arg TERRASCAN_VERSION=1.10.0 \
--build-arg TFLINT_VERSION=0.31.0 \
--build-arg TFSEC_VERSION=latest \
--build-arg TFUPDATE_VERSION=latest \
.
```

Expand All @@ -114,7 +117,7 @@ Set `-e PRE_COMMIT_COLOR=never` to disable the color output in `pre-commit`.
[`coreutils`](https://formulae.brew.sh/formula/coreutils) is required for hooks on MacOS (due to use of `realpath`).

```bash
brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan infracost jq
brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan infracost tfupdate jq
```

</details>
Expand All @@ -135,6 +138,7 @@ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/late
curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init
sudo apt install -y jq && \
curl -L "$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > infracost.tgz && tar -xzf infracost.tgz && rm infracost.tgz && sudo mv infracost-linux-amd64 /usr/bin/infracost && infracost register
curl -L "$(curl -s https://api.github.com/repos/minamijoyo/tfupdate/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > tfupdate.tar.gz && tar -xzf tfupdate.tar.gz tfupdate && rm tfupdate.tar.gz && sudo mv tfupdate /usr/bin/
```

</details>
Expand All @@ -154,6 +158,7 @@ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/release
curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/
sudo apt install -y jq && \
curl -L "$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > infracost.tgz && tar -xzf infracost.tgz && rm infracost.tgz && sudo mv infracost-linux-amd64 /usr/bin/infracost && infracost register
curl -L "$(curl -s https://api.github.com/repos/minamijoyo/tfupdate/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > tfupdate.tar.gz && tar -xzf tfupdate.tar.gz tfupdate && rm tfupdate.tar.gz && sudo mv tfupdate /usr/bin/
```

</details>
Expand Down Expand Up @@ -227,7 +232,8 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform
| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - |
| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` |
| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` |
| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. [Hook notes](#terrascan) | `terrascan` |
| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. [Hook notes](#terrascan) | `terrascan` |
| `tfupdate` | [tfupdate](https://github.com/minamijoyo/tfupdate) Update version constraints of Terraform core, providers, and modules. [Hook notes](#tfupdate) | `tfupdate` |
<!-- markdownlint-enable no-inline-html -->

Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook.
Expand Down Expand Up @@ -620,6 +626,33 @@ Example:
3. Use `--skip-rules="ruleID1,ruleID2"` parameter to skip one or more rules globally while scanning (e.g.: `--args=--skip-rules="ruleID1,ruleID2"`).
4. Use the syntax `#ts:skip=RuleID optional_comment` inside a resource to skip the rule for that resource.

### tfupdate

1. Out of the box `tfupdate` will pin the terraform version:

```yaml
- id: tfupdate
name: Autoupdate Terraform versions
```

2. If you'd like to pin providers, etc., use custom arguments, i.e `provider=PROVIDER_NAME`:

```yaml
- id: tfupdate
name: Autoupdate AWS provider versions
args:
- --args=provider aws # Will be pined to latest version

- id: tfupdate
name: Autoupdate Helm provider versions
args:
- --args=provider helm
- --args=--version 2.5.0 # Will be pined to specified version
```

Check [`tfupdate` usage instructions](https://github.com/minamijoyo/tfupdate#usage) for other available options and usage examples.
No need to pass `--recursive .` as it is added automatically.

## Authors

This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from these awesome contributors:
Expand Down
61 changes: 61 additions & 0 deletions hooks/tfupdate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -eo pipefail

# globals variables
# hook ID, see `- id` for details in .pre-commit-hooks.yaml file
readonly HOOK_ID='tfupdate'
# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
# shellcheck source=_common.sh
. "$SCRIPT_DIR/_common.sh"

function main {
common::initialize "$SCRIPT_DIR"
common::parse_cmdline "$@"
# shellcheck disable=SC2153 # False positive
common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}"
}
#######################################################################
# Unique part of `common::per_dir_hook`. The function is executed in loop
# on each provided dir path. Run wrapped tool with specified arguments
# Arguments:
# args (string with array) arguments that configure wrapped tool behavior
# dir_path (string) PATH to dir relative to git repo root.
# Can be used in error logging
# Outputs:
# If failed - print out hook checks status
#######################################################################
function per_dir_hook_unique_part {
local -r args="$1"
# shellcheck disable=SC2034 # Unused var.
local -r dir_path="$2"

# pass the arguments to hook
# shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]")
tfupdate ${args[@]} "${dir_path}"

# return exit code to common::per_dir_hook
local exit_code=$?
return $exit_code
}

#######################################################################
# Unique part of `common::per_dir_hook`. The function is executed one time
# in the root git repo
# Arguments:
# args (string with array) arguments that configure wrapped tool behavior
#######################################################################
function run_hook_on_whole_repo {
local -r args="$1"
# pass the arguments to hook
# shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]")
# shellcheck disable=SC2048 # Use "${array[@]}" (with quotes) to prevent whitespace problems.
# shellcheck disable=SC2086 # Double quote to prevent globbing and word splitting.
tfupdate ${args[*]} --recursive .

# return exit code to common::per_dir_hook
local exit_code=$?
return $exit_code
}

[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@"