diff --git a/.dockerignore b/.dockerignore
index a78e675d2..e57569568 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,3 +2,4 @@
 !.dockerignore
 !Dockerfile
 !tools/entrypoint.sh
+!tools/install/*.sh
diff --git a/.github/.container-structure-test-config.yaml b/.github/.container-structure-test-config.yaml
index 8cbf7e4de..c746d83fc 100644
--- a/.github/.container-structure-test-config.yaml
+++ b/.github/.container-structure-test-config.yaml
@@ -13,7 +13,7 @@ commandTests:
   - name: "terraform"
     command: "terraform"
     args: ["-version"]
-    expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\non linux_amd64\\n$"]
+    expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\n"]
 
   - name: "gcc"
     command: "gcc"
@@ -28,12 +28,12 @@ commandTests:
   - name: "infracost"
     command: "infracost"
     args: ["--version"]
-    expectedOutput: ["^Infracost v([0-9]+\\.){2}[0-9]+\\n$"]
+    expectedOutput: ["^Infracost v([0-9]+\\.){2}[0-9]+"]
 
   - name: "terraform-docs"
     command: "terraform-docs"
     args: ["--version"]
-    expectedOutput: ["^terraform-docs version v([0-9]+\\.){2}[0-9]+ [a-z0-9]+ linux/amd64\\n$"]
+    expectedOutput: ["^terraform-docs version v([0-9]+\\.){2}[0-9]+ [a-z0-9]+"]
 
   - name: "terragrunt"
     command: "terragrunt"
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 388bf3751..1d1568e51 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -113,6 +113,9 @@ You can use [this PR](https://github.com/antonbabenko/pre-commit-terraform/pull/
 
 ### Add code
 
+> [!TIP]
+> Here is a screencast of [how to add new dependency in `tools/install/`](https://github.com/antonbabenko/pre-commit-terraform/assets/11096782/8fc461e9-f163-4592-9497-4a18fa89c0e8) - used in Dockerfile
+
 1. Based on prev. block, add hook dependencies installation to [Dockerfile](../Dockerfile).  
     Check that works:
     * `docker build -t pre-commit --build-arg INSTALL_ALL=true .`
diff --git a/.github/workflows/build-image-test.yaml b/.github/workflows/build-image-test.yaml
index 9dfbda3c6..05fbc37b0 100644
--- a/.github/workflows/build-image-test.yaml
+++ b/.github/workflows/build-image-test.yaml
@@ -27,6 +27,13 @@ jobs:
             .dockerignore
             tools/entrypoint.sh
             .github/workflows/build-image-test.yaml
+            tools/*.sh
+
+      - name: Set up QEMU
+        if: matrix.os != 'ubuntu-latest' || matrix.arch != 'amd64'
+        uses: docker/setup-qemu-action@v2
+        with:
+          platforms: 'arm64'
 
       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
@@ -38,7 +45,7 @@ jobs:
           context: .
           build-args: |
             INSTALL_ALL=true
-          platforms: linux/amd64 # Only one allowed here, see https://github.com/docker/buildx/issues/59#issuecomment-1433097926
+          platforms: linux/${{ matrix.arch }} # Only one allowed here, see https://github.com/docker/buildx/issues/59#issuecomment-1433097926
           push: false
           load: true
           tags: |
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 17fc5a6fc..dee8870ae 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -42,13 +42,14 @@ repos:
   hooks:
     - id: hadolint
       args: [
-        '--ignore', 'DL3027', # Do not use apt
         '--ignore', 'DL3007', # Using latest
+        '--ignore', 'DL3013', # Pin versions in pip
+        '--ignore', 'DL3027', # Do not use apt
+        '--ignore', 'DL3059', # Docker `RUN`s shouldn't be consolidated here
         '--ignore', 'DL4006', # Not related to alpine
         '--ignore', 'SC1091', # Useless check
         '--ignore', 'SC2015', # Useless check
         '--ignore', 'SC3037', # Not related to alpine
-        '--ignore', 'DL3013', # Pin versions in pip
       ]
 
 # JSON5 Linter
diff --git a/Dockerfile b/Dockerfile
index 242a45405..3822194e5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,41 +7,43 @@ WORKDIR /bin_dir
 
 RUN apk add --no-cache \
     # Builder deps
+    bash=~5 \
     curl=~8 && \
     # Upgrade packages for be able get latest Checkov
     python3 -m pip install --no-cache-dir --upgrade \
         pip \
         setuptools
 
+COPY tools/install/ /install/
+
+#
+# Install required tools
+#
 ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest}
 ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-latest}
 
-# Install pre-commit
-RUN if [ ${PRE_COMMIT_VERSION} = "latest" ]; \
-        then pip3 install --no-cache-dir pre-commit; \
-        else pip3 install --no-cache-dir pre-commit==${PRE_COMMIT_VERSION}; \
+RUN touch /.env && \
+    if [ "$PRE_COMMIT_VERSION" = "false" ] || [ "$TERRAFORM_VERSION" = "false" ]; then \
+        echo "Vital software can't be skipped" && exit 1; \
     fi
 
-# Install terraform because pre-commit needs it
-RUN if [ "${TERRAFORM_VERSION}" = "latest" ]; then \
-        TERRAFORM_VERSION="$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | grep -o -E -m 1 "[0-9.]+")" \
-    ; fi && \
-    curl -L "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip" > terraform.zip && \
-    unzip terraform.zip terraform && rm terraform.zip
+
+RUN /install/pre-commit.sh
+RUN /install/terraform.sh
 
 #
 # Install tools
 #
 ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false}
+ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}
 ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false}
 ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false}
 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 TRIVY_VERSION=${TRIVY_VERSION:-false}
 ARG TFUPDATE_VERSION=${TFUPDATE_VERSION:-false}
-ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}
+ARG TRIVY_VERSION=${TRIVY_VERSION:-false}
 
 
 # Tricky thing to install all tools by set only one arg.
@@ -49,167 +51,45 @@ ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}
 # specified in step below
 ARG INSTALL_ALL=${INSTALL_ALL:-false}
 RUN if [ "$INSTALL_ALL" != "false" ]; then \
-        echo "export CHECKOV_VERSION=latest" >> /.env && \
-        echo "export INFRACOST_VERSION=latest" >> /.env && \
-        echo "export TERRAFORM_DOCS_VERSION=latest" >> /.env && \
-        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 TRIVY_VERSION=latest" >> /.env && \
-        echo "export TFUPDATE_VERSION=latest" >> /.env && \
-        echo "export HCLEDIT_VERSION=latest" >> /.env \
-    ; else \
-        touch /.env \
+        echo "CHECKOV_VERSION=latest"        >> /.env && \
+        echo "HCLEDIT_VERSION=latest"        >> /.env && \
+        echo "INFRACOST_VERSION=latest"      >> /.env && \
+        echo "TERRAFORM_DOCS_VERSION=latest" >> /.env && \
+        echo "TERRAGRUNT_VERSION=latest"     >> /.env && \
+        echo "TERRASCAN_VERSION=latest"      >> /.env && \
+        echo "TFLINT_VERSION=latest"         >> /.env && \
+        echo "TFSEC_VERSION=latest"          >> /.env && \
+        echo "TFUPDATE_VERSION=latest"       >> /.env && \
+        echo "TRIVY_VERSION=latest"          >> /.env \
     ; fi
 
-
-# Checkov
-RUN . /.env && \
-    if [ "$CHECKOV_VERSION" != "false" ]; then \
-    ( \
-        # cargo, gcc, git, musl-dev, rust and CARGO envvar required for compilation of rustworkx@0.13.2, no longer required once checkov version depends on rustworkx >0.14.0
-        # https://github.com/bridgecrewio/checkov/pull/6045
-        # gcc libffi-dev musl-dev required for compilation of cffi, until it contains musl aarch64
-        export CARGO_NET_GIT_FETCH_WITH_CLI=true && \
-        apk add --no-cache cargo=~1 gcc=~12 git=~2 libffi-dev=~3 libgcc=~12 musl-dev=~1 rust=~1 ; \
-        if [ "$CHECKOV_VERSION" = "latest" ]; \
-            then pip3 install --no-cache-dir checkov || exit 1; \
-            else pip3 install --no-cache-dir checkov==${CHECKOV_VERSION} || exit 1; \
-        fi; \
-        apk del cargo gcc git libffi-dev musl-dev rust \
-    ) \
-    ; fi
-
-# infracost
-RUN . /.env && \
-    if [ "$INFRACOST_VERSION" != "false" ]; then \
-    ( \
-        INFRACOST_RELEASES="https://api.github.com/repos/infracost/infracost/releases" && \
-        if [ "$INFRACOST_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${INFRACOST_RELEASES}/latest | grep -o -E -m 1 "https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz")" > infracost.tgz; \
-            else curl -L "$(curl -s ${INFRACOST_RELEASES} | grep -o -E "https://.+?v${INFRACOST_VERSION}/infracost-${TARGETOS}-${TARGETARCH}.tar.gz")" > infracost.tgz; \
-        fi; \
-    ) && tar -xzf infracost.tgz && rm infracost.tgz && mv infracost-${TARGETOS}-${TARGETARCH} infracost \
-    ; fi
-
-# Terraform docs
-RUN . /.env && \
-    if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then \
-    ( \
-        TERRAFORM_DOCS_RELEASES="https://api.github.com/repos/terraform-docs/terraform-docs/releases" && \
-        if [ "$TERRAFORM_DOCS_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES}/latest | grep -o -E -m 1 "https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz")" > terraform-docs.tgz; \
-            else curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES} | grep -o -E "https://.+?v${TERRAFORM_DOCS_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz")" > terraform-docs.tgz; \
-        fi; \
-    ) && tar -xzf terraform-docs.tgz terraform-docs && rm terraform-docs.tgz && chmod +x terraform-docs \
-    ; fi
-
-# Terragrunt
-RUN . /.env \
-    && if [ "$TERRAGRUNT_VERSION" != "false" ]; then \
-    ( \
-        TERRAGRUNT_RELEASES="https://api.github.com/repos/gruntwork-io/terragrunt/releases" && \
-        if [ "$TERRAGRUNT_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TERRAGRUNT_RELEASES}/latest | grep -o -E -m 1 "https://.+?/terragrunt_${TARGETOS}_${TARGETARCH}")" > terragrunt; \
-            else curl -L "$(curl -s ${TERRAGRUNT_RELEASES} | grep -o -E -m 1 "https://.+?v${TERRAGRUNT_VERSION}/terragrunt_${TARGETOS}_${TARGETARCH}")" > terragrunt; \
-        fi; \
-    ) && chmod +x terragrunt \
-    ; fi
+RUN /install/checkov.sh
+RUN /install/hcledit.sh
+RUN /install/infracost.sh
+RUN /install/terraform-docs.sh
+RUN /install/terragrunt.sh
+RUN /install/terrascan.sh
+RUN /install/tflint.sh
+RUN /install/tfsec.sh
+RUN /install/tfupdate.sh
+RUN /install/trivy.sh
 
 
-# Terrascan
-RUN . /.env && \
-    if [ "$TERRASCAN_VERSION" != "false" ]; then \
-    if [ "$TARGETARCH" != "amd64" ]; then ARCH="$TARGETARCH"; else ARCH="x86_64"; fi; \
-    # Convert the first letter to Uppercase
-    OS="$(echo ${TARGETOS} | cut -c1 | tr '[:lower:]' '[:upper:]' | xargs echo -n; echo ${TARGETOS} | cut -c2-)"; \
-    ( \
-        TERRASCAN_RELEASES="https://api.github.com/repos/tenable/terrascan/releases" && \
-        if [ "$TERRASCAN_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TERRASCAN_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${OS}_${ARCH}.tar.gz")" > terrascan.tar.gz; \
-            else curl -L "$(curl -s ${TERRASCAN_RELEASES} | grep -o -E "https://.+?${TERRASCAN_VERSION}_${OS}_${ARCH}.tar.gz")" > terrascan.tar.gz; \
-        fi; \
-    ) && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && \
-    ./terrascan init \
-    ; fi
-
-# TFLint
-RUN . /.env && \
-    if [ "$TFLINT_VERSION" != "false" ]; then \
-    ( \
-        TFLINT_RELEASES="https://api.github.com/repos/terraform-linters/tflint/releases" && \
-        if [ "$TFLINT_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TFLINT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.zip")" > tflint.zip; \
-            else curl -L "$(curl -s ${TFLINT_RELEASES} | grep -o -E "https://.+?/v${TFLINT_VERSION}/tflint_${TARGETOS}_${TARGETARCH}.zip")" > tflint.zip; \
-        fi; \
-    ) && unzip tflint.zip && rm tflint.zip \
-    ; fi
-
-# TFSec
-RUN . /.env && \
-    if [ "$TFSEC_VERSION" != "false" ]; then \
-    ( \
-        TFSEC_RELEASES="https://api.github.com/repos/aquasecurity/tfsec/releases" && \
-        if [ "$TFSEC_VERSION" = "latest" ]; then \
-            curl -L "$(curl -s ${TFSEC_RELEASES}/latest | grep -o -E -m 1 "https://.+?/tfsec-${TARGETOS}-${TARGETARCH}")" > tfsec; \
-            else curl -L "$(curl -s ${TFSEC_RELEASES} | grep -o -E -m 1 "https://.+?v${TFSEC_VERSION}/tfsec-${TARGETOS}-${TARGETARCH}")" > tfsec; \
-        fi; \
-    ) && chmod +x tfsec \
-    ; fi
-
-# Trivy
-RUN . /.env && \
-    if [ "$TRIVY_VERSION" != "false" ]; then \
-    if [ "$TARGETARCH" != "amd64" ]; then ARCH="$TARGETARCH"; else ARCH="64bit"; fi; \
-    ( \
-        TRIVY_RELEASES="https://api.github.com/repos/aquasecurity/trivy/releases" && \
-        if [ "$TRIVY_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TRIVY_RELEASES}/latest | grep -o -E -i -m 1 "https://.+?/trivy_.+?_${TARGETOS}-${ARCH}.tar.gz")" > trivy.tar.gz; \
-            else curl -L "$(curl -s ${TRIVY_RELEASES} | grep -o -E -i -m 1 "https://.+?/v${TRIVY_VERSION}/trivy_.+?_${TARGETOS}-${ARCH}.tar.gz")" > trivy.tar.gz; \
-        fi; \
-    ) && tar -xzf trivy.tar.gz trivy && rm trivy.tar.gz \
-    ; fi
-
-# TFUpdate
-RUN . /.env && \
-    if [ "$TFUPDATE_VERSION" != "false" ]; then \
-    ( \
-        TFUPDATE_RELEASES="https://api.github.com/repos/minamijoyo/tfupdate/releases" && \
-        if [ "$TFUPDATE_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${TFUPDATE_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz")" > tfupdate.tgz; \
-            else curl -L "$(curl -s ${TFUPDATE_RELEASES} | grep -o -E -m 1 "https://.+?${TFUPDATE_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz")" > tfupdate.tgz; \
-        fi; \
-    ) && tar -xzf tfupdate.tgz tfupdate && rm tfupdate.tgz \
-    ; fi
-
-# hcledit
-RUN . /.env && \
-    if [ "$HCLEDIT_VERSION" != "false" ]; then \
-    ( \
-        HCLEDIT_RELEASES="https://api.github.com/repos/minamijoyo/hcledit/releases" && \
-        if [ "$HCLEDIT_VERSION" = "latest" ]; \
-            then curl -L "$(curl -s ${HCLEDIT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz")" > hcledit.tgz; \
-            else curl -L "$(curl -s ${HCLEDIT_RELEASES} | grep -o -E -m 1 "https://.+?${HCLEDIT_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz")" > hcledit.tgz; \
-        fi; \
-    ) && tar -xzf hcledit.tgz hcledit && rm hcledit.tgz \
-    ; fi
-
 # Checking binaries versions and write it to debug file
 RUN . /.env && \
     F=tools_versions_info && \
     pre-commit --version >> $F && \
     ./terraform --version | head -n 1 >> $F && \
     (if [ "$CHECKOV_VERSION"        != "false" ]; then echo "checkov $(checkov --version)" >> $F;     else echo "checkov SKIPPED" >> $F        ; fi) && \
+    (if [ "$HCLEDIT_VERSION"        != "false" ]; then echo "hcledit $(./hcledit version)" >> $F;     else echo "hcledit SKIPPED" >> $F        ; fi) && \
     (if [ "$INFRACOST_VERSION"      != "false" ]; then echo "$(./infracost --version)" >> $F;         else echo "infracost SKIPPED" >> $F      ; fi) && \
     (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F;              else echo "terraform-docs SKIPPED" >> $F ; fi) && \
     (if [ "$TERRAGRUNT_VERSION"     != "false" ]; then ./terragrunt --version >> $F;                  else echo "terragrunt SKIPPED" >> $F     ; fi) && \
     (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 [ "$TRIVY_VERSION"          != "false" ]; then echo "trivy $(./trivy --version)" >> $F;       else echo "trivy SKIPPED" >> $F          ; fi) && \
     (if [ "$TFUPDATE_VERSION"       != "false" ]; then echo "tfupdate $(./tfupdate --version)" >> $F; else echo "tfupdate SKIPPED" >> $F       ; fi) && \
-    (if [ "$HCLEDIT_VERSION"        != "false" ]; then echo "hcledit $(./hcledit version)" >> $F;     else echo "hcledit SKIPPED" >> $F       ; fi) && \
+    (if [ "$TRIVY_VERSION"          != "false" ]; then echo "trivy $(./trivy --version)" >> $F;       else echo "trivy SKIPPED" >> $F          ; fi) && \
     echo -e "\n\n" && cat $F && echo -e "\n\n"
 
 
diff --git a/hooks/infracost_breakdown.sh b/hooks/infracost_breakdown.sh
index 551579112..d5351b9bf 100755
--- a/hooks/infracost_breakdown.sh
+++ b/hooks/infracost_breakdown.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_checkov.sh b/hooks/terraform_checkov.sh
index a9603afd9..dbbdc463b 100755
--- a/hooks/terraform_checkov.sh
+++ b/hooks/terraform_checkov.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_docs.sh b/hooks/terraform_docs.sh
index c597730b0..472d6487c 100755
--- a/hooks/terraform_docs.sh
+++ b/hooks/terraform_docs.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_fmt.sh b/hooks/terraform_fmt.sh
index 727635caa..949a066a8 100755
--- a/hooks/terraform_fmt.sh
+++ b/hooks/terraform_fmt.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh
index bf2aa2084..34bce58b2 100755
--- a/hooks/terraform_providers_lock.sh
+++ b/hooks/terraform_providers_lock.sh
@@ -3,8 +3,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh
index 9c8373676..211e648bc 100755
--- a/hooks/terraform_tflint.sh
+++ b/hooks/terraform_tflint.sh
@@ -3,8 +3,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh
index 75966bf8c..52cab2c71 100755
--- a/hooks/terraform_tfsec.sh
+++ b/hooks/terraform_tfsec.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_trivy.sh b/hooks/terraform_trivy.sh
index fd9a3203a..7de40188b 100755
--- a/hooks/terraform_trivy.sh
+++ b/hooks/terraform_trivy.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_validate.sh b/hooks/terraform_validate.sh
index 9a6c50fce..a56e8c6db 100755
--- a/hooks/terraform_validate.sh
+++ b/hooks/terraform_validate.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terraform_wrapper_module_for_each.sh b/hooks/terraform_wrapper_module_for_each.sh
index e9a98e2a3..945bf9a8a 100755
--- a/hooks/terraform_wrapper_module_for_each.sh
+++ b/hooks/terraform_wrapper_module_for_each.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh
index 1effbf47b..a39c78a79 100755
--- a/hooks/terragrunt_fmt.sh
+++ b/hooks/terragrunt_fmt.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terragrunt_providers_lock.sh b/hooks/terragrunt_providers_lock.sh
index f05a571c4..059f8d1f5 100755
--- a/hooks/terragrunt_providers_lock.sh
+++ b/hooks/terragrunt_providers_lock.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh
index 8fafada0a..e595329b6 100755
--- a/hooks/terragrunt_validate.sh
+++ b/hooks/terragrunt_validate.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh
index 0c1447bd4..d7dc5f4a5 100755
--- a/hooks/terrascan.sh
+++ b/hooks/terrascan.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/hooks/tfupdate.sh b/hooks/tfupdate.sh
index 9397cfe94..5c9979a47 100755
--- a/hooks/tfupdate.sh
+++ b/hooks/tfupdate.sh
@@ -2,8 +2,8 @@
 set -eo pipefail
 
 # globals variables
-# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
-readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
 # shellcheck source=_common.sh
 . "$SCRIPT_DIR/_common.sh"
 
diff --git a/tools/install/_common.sh b/tools/install/_common.sh
new file mode 100755
index 000000000..70297f297
--- /dev/null
+++ b/tools/install/_common.sh
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+
+set -eo pipefail
+
+# Tool name, based on filename.
+# Tool filename MUST BE same as in package manager/binary name
+TOOL=${0##*/}
+readonly TOOL=${TOOL%%.*}
+
+# Get "TOOL_VERSION"
+# shellcheck disable=SC1091 # Created in Dockerfile before execution of this script
+source /.env
+env_var_name="${TOOL//-/_}"
+env_var_name="${env_var_name^^}_VERSION"
+# shellcheck disable=SC2034 # Used in other scripts
+readonly VERSION="${!env_var_name}"
+
+# Skip tool installation if the version is set to "false"
+if [[ $VERSION == false ]]; then
+  echo "'$TOOL' skipped"
+  exit 0
+fi
+
+#######################################################################
+# Install the latest or specific version of the tool from GitHub release
+# Globals:
+#   TOOL - Name of the tool
+#   VERSION - Version of the tool
+# Arguments:
+#   GH_ORG - GitHub organization name where the tool is hosted
+#   DISTRIBUTED_AS - How the tool is distributed.
+#     Can be: 'tar.gz', 'zip' or 'binary'
+#   GH_RELEASE_REGEX_LATEST - Regular expression to match the latest
+#     release URL
+#   GH_RELEASE_REGEX_SPECIFIC_VERSION - Regular expression to match the
+#      specific version release URL
+#   UNUSUAL_TOOL_NAME_IN_PKG - If the tool in the tar.gz package is
+#     not in the root or named differently than the tool name itself,
+#     For example, includes the version number or is in a subdirectory
+#######################################################################
+function common::install_from_gh_release {
+  local -r GH_ORG=$1
+  local -r DISTRIBUTED_AS=$2
+  local -r GH_RELEASE_REGEX_LATEST=$3
+  local -r GH_RELEASE_REGEX_SPECIFIC_VERSION=$4
+  local -r UNUSUAL_TOOL_NAME_IN_PKG=$5
+
+  case $DISTRIBUTED_AS in
+    tar.gz | zip)
+      local -r PKG="${TOOL}.${DISTRIBUTED_AS}"
+      ;;
+    binary)
+      local -r PKG="$TOOL"
+      ;;
+    *)
+      echo "Unknown DISTRIBUTED_AS: '$DISTRIBUTED_AS'. Should be one of: 'tar.gz', 'zip' or 'binary'." >&2
+      exit 1
+      ;;
+  esac
+
+  # Download tool
+  local -r RELEASES="https://api.github.com/repos/${GH_ORG}/${TOOL}/releases"
+
+  if [[ $VERSION == latest ]]; then
+    curl -L "$(curl -s "${RELEASES}/latest" | grep -o -E -i -m 1 "$GH_RELEASE_REGEX_LATEST")" > "$PKG"
+  else
+    curl -L "$(curl -s "$RELEASES" | grep -o -E -i -m 1 "$GH_RELEASE_REGEX_SPECIFIC_VERSION")" > "$PKG"
+  fi
+
+  # Make tool ready to use
+  if [[ $DISTRIBUTED_AS == tar.gz ]]; then
+    if [[ -z $UNUSUAL_TOOL_NAME_IN_PKG ]]; then
+      tar -xzf "$PKG" "$TOOL"
+    else
+      tar -xzf "$PKG" "$UNUSUAL_TOOL_NAME_IN_PKG"
+      mv "$UNUSUAL_TOOL_NAME_IN_PKG" "$TOOL"
+    fi
+    rm "$PKG"
+
+  elif [[ $DISTRIBUTED_AS == zip ]]; then
+    unzip "$PKG"
+    rm "$PKG"
+  else
+    chmod +x "$PKG"
+  fi
+}
diff --git a/tools/install/checkov.sh b/tools/install/checkov.sh
new file mode 100755
index 000000000..8be8c649e
--- /dev/null
+++ b/tools/install/checkov.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+apk add --no-cache \
+  gcc=~12 \
+  libffi-dev=~3 \
+  musl-dev=~1
+
+# cargo, gcc, git, musl-dev, rust and CARGO envvar required for compilation of rustworkx@0.13.2
+# no longer required once checkov version depends on rustworkx >0.14.0
+# https://github.com/bridgecrewio/checkov/pull/6045
+# gcc libffi-dev musl-dev required for compilation of cffi, until it contains musl aarch64
+export CARGO_NET_GIT_FETCH_WITH_CLI=true
+apk add --no-cache \
+  cargo=~1 \
+  git=~2 \
+  libgcc=~12 \
+  rust=~1
+
+if [[ $VERSION == latest ]]; then
+  pip3 install --no-cache-dir "${TOOL}"
+else
+  pip3 install --no-cache-dir "${TOOL}==${VERSION}"
+fi
+
+apk del gcc libffi-dev musl-dev
+# no longer required once checkov version depends on rustworkx >0.14.0
+# https://github.com/bridgecrewio/checkov/pull/6045
+apk del cargo git rust
diff --git a/tools/install/hcledit.sh b/tools/install/hcledit.sh
new file mode 100755
index 000000000..498e4fb6f
--- /dev/null
+++ b/tools/install/hcledit.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="minamijoyo"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/infracost.sh b/tools/install/infracost.sh
new file mode 100755
index 000000000..9974ca1d1
--- /dev/null
+++ b/tools/install/infracost.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="infracost"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}-${TARGETOS}-${TARGETARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+UNUSUAL_TOOL_NAME_IN_PKG="${TOOL}-${TARGETOS}-${TARGETARCH}"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" \
+  "$UNUSUAL_TOOL_NAME_IN_PKG"
diff --git a/tools/install/pre-commit.sh b/tools/install/pre-commit.sh
new file mode 100755
index 000000000..9f3bdfb24
--- /dev/null
+++ b/tools/install/pre-commit.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+if [[ $VERSION == latest ]]; then
+  pip3 install --no-cache-dir "$TOOL"
+else
+  pip3 install --no-cache-dir "${TOOL}==${VERSION}"
+fi
diff --git a/tools/install/terraform-docs.sh b/tools/install/terraform-docs.sh
new file mode 100755
index 000000000..9eec05394
--- /dev/null
+++ b/tools/install/terraform-docs.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="terraform-docs"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/terraform.sh b/tools/install/terraform.sh
new file mode 100755
index 000000000..65ec21c2b
--- /dev/null
+++ b/tools/install/terraform.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+# shellcheck disable=SC2153 # We are using the variable from _common.sh
+if [[ $VERSION == latest ]]; then
+  version="$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | grep -o -E -m 1 "[0-9.]+")"
+else
+  version=$VERSION
+fi
+readonly version
+
+curl -L "https://releases.hashicorp.com/terraform/${version}/${TOOL}_${version}_${TARGETOS}_${TARGETARCH}.zip" > "${TOOL}.zip"
+unzip "${TOOL}.zip" "$TOOL"
+rm "${TOOL}.zip"
diff --git a/tools/install/terragrunt.sh b/tools/install/terragrunt.sh
new file mode 100755
index 000000000..20cc60ff7
--- /dev/null
+++ b/tools/install/terragrunt.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="gruntwork-io"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}_${TARGETOS}_${TARGETARCH}"
+GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}_${TARGETOS}_${TARGETARCH}"
+DISTRIBUTED_AS="binary"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/terrascan.sh b/tools/install/terrascan.sh
new file mode 100755
index 000000000..4393159d3
--- /dev/null
+++ b/tools/install/terrascan.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+[[ $TARGETARCH == amd64 ]] && ARCH="x86_64" || ARCH="$TARGETARCH"
+readonly ARCH
+# Convert the first letter to Uppercase
+OS="${TARGETOS^}"
+
+GH_ORG="tenable"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${OS}_${ARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?_${OS}_${ARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
+
+# Download (caching) terrascan rego policies to save time during terrascan run
+# https://runterrascan.io/docs/usage/_print/#pg-2cba380a2ef14e4ae3c674e02c5f9f53
+./"$TOOL" init
diff --git a/tools/install/tflint.sh b/tools/install/tflint.sh
new file mode 100755
index 000000000..ac2556b81
--- /dev/null
+++ b/tools/install/tflint.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="terraform-linters"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?/v${VERSION}/${TOOL}_${TARGETOS}_${TARGETARCH}.zip"
+GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.zip"
+DISTRIBUTED_AS="zip"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/tfsec.sh b/tools/install/tfsec.sh
new file mode 100755
index 000000000..3c9c2430d
--- /dev/null
+++ b/tools/install/tfsec.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="aquasecurity"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}-${TARGETOS}-${TARGETARCH}"
+GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}-${TARGETOS}-${TARGETARCH}"
+DISTRIBUTED_AS="binary"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/tfupdate.sh b/tools/install/tfupdate.sh
new file mode 100755
index 000000000..498e4fb6f
--- /dev/null
+++ b/tools/install/tfupdate.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+GH_ORG="minamijoyo"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"
diff --git a/tools/install/trivy.sh b/tools/install/trivy.sh
new file mode 100755
index 000000000..c07625b53
--- /dev/null
+++ b/tools/install/trivy.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly SCRIPT_DIR
+# shellcheck source=_common.sh
+. "$SCRIPT_DIR/_common.sh"
+
+#
+# Unique part
+#
+
+[[ $TARGETARCH == amd64 ]] && ARCH="64bit" || ARCH="$TARGETARCH"
+readonly ARCH
+
+GH_ORG="aquasecurity"
+GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?/v${VERSION}/${TOOL}_.+?_${TARGETOS}-${ARCH}.tar.gz"
+GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}_.+?_${TARGETOS}-${ARCH}.tar.gz"
+DISTRIBUTED_AS="tar.gz"
+
+common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \
+  "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"