Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion src/terraform/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "terraform",
"version": "1.4.0",
"version": "1.4.1",
"name": "Terraform, tflint, and TFGrunt",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform",
"description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.",
Expand Down
83 changes: 73 additions & 10 deletions src/terraform/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi

import_hashicorp_gpg_key_noble() {
unset GNUPGHOME
curl -fsSL https://keybase.io/hashicorp/pgp_keys.asc | gpg --import
if ! gpg --list-keys "${TERRAFORM_GPG_KEY}" > /dev/null 2>&1; then
gpg --list-keys
echo "(!) Error: HashiCorp GPG key not found in keyring after import."
echo " Please check your network connection and ensure that the keyserver is reachable."
echo " Alternatively, you can use Ubuntu jammy(22.04) or debian bookworm(12) as the base image."
exit 1
fi
}

# Detect Ubuntu Noble and use new repo setup, else use legacy GPG logic
IS_NOBLE=0
if grep -qi 'ubuntu' /etc/os-release; then
. /etc/os-release
if [[ "$VERSION_CODENAME" == "noble" ]]; then
IS_NOBLE=1
fi
fi

# Get the list of GPG key servers that are reachable
get_gpg_key_servers() {
declare -A keyservers_curl_map=(
Expand Down Expand Up @@ -366,6 +387,25 @@ install_terraform() {
curl -sSL -o ${terraform_filename} "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
}

verify_signature() {
local gpg_key=$1
local sha256sums_url=$2
local sig_url=$3
local sha256sums_file=$4
local sig_file=$5

receive_gpg_keys "$gpg_key"
curl -sSL -o "$sha256sums_file" "$sha256sums_url"
curl -sSL -o "$sig_file" "$sig_url"
gpg --verify "$sig_file" "$sha256sums_file"
}

verify_terraform_sig() {
local sha256sums_url="${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS"
local sig_url="${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig"
verify_signature TERRAFORM_GPG_KEY "$sha256sums_url" "$sig_url" "terraform_SHA256SUMS" "terraform_SHA256SUMS.sig"
}

mkdir -p /tmp/tf-downloads
cd /tmp/tf-downloads
# Install Terraform, tflint, Terragrunt
Expand All @@ -378,10 +418,18 @@ if grep -q "The specified key does not exist." "${terraform_filename}"; then
fi
if [ "${TERRAFORM_SHA256}" != "dev-mode" ]; then
if [ "${TERRAFORM_SHA256}" = "automatic" ]; then
receive_gpg_keys TERRAFORM_GPG_KEY
curl -sSL -o terraform_SHA256SUMS "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS"
curl -sSL -o terraform_SHA256SUMS.sig "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig"
gpg --verify terraform_SHA256SUMS.sig terraform_SHA256SUMS
if [ "$IS_NOBLE" -eq 1 ]; then
import_hashicorp_gpg_key_noble
curl -sSL -o terraform_SHA256SUMS "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS"
curl -sSL -o terraform_SHA256SUMS.sig "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig"
gpg --list-keys
if ! gpg --verify terraform_SHA256SUMS.sig terraform_SHA256SUMS; then
echo "Primary GPG verification failed, attempting fallback verification..."
verify_terraform_sig
fi
else
verify_terraform_sig
fi
else
echo "${TERRAFORM_SHA256} *${terraform_filename}" > terraform_SHA256SUMS
fi
Expand Down Expand Up @@ -443,6 +491,12 @@ if [ "${TFLINT_VERSION}" != "none" ]; then
mv -f tflint /usr/local/bin/
fi

verify_sentinel_sig() {
local sha256sums_url="${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS"
local sig_url="${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig"
verify_signature TERRAFORM_GPG_KEY "$sha256sums_url" "$sig_url" "sentinel_checksums.txt" "sentinel_checksums.txt.sig"
}

install_terragrunt() {
TERRAGRUNT_VERSION=$1
curl -sSL -o /tmp/tf-downloads/${terragrunt_filename} https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/${terragrunt_filename}
Expand Down Expand Up @@ -477,12 +531,21 @@ if [ "${INSTALL_SENTINEL}" = "true" ]; then
curl -sSL -o /tmp/tf-downloads/${sentinel_filename} ${sentinel_releases_url}/${SENTINEL_VERSION}/${sentinel_filename}
if [ "${SENTINEL_SHA256}" != "dev-mode" ]; then
if [ "${SENTINEL_SHA256}" = "automatic" ]; then
receive_gpg_keys TERRAFORM_GPG_KEY
curl -sSL -o sentinel_checksums.txt ${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS
curl -sSL -o sentinel_checksums.txt.sig ${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig
gpg --verify sentinel_checksums.txt.sig sentinel_checksums.txt
# Verify the SHASUM matches the archive
shasum -a 256 --ignore-missing -c sentinel_checksums.txt
if [ "$IS_NOBLE" -eq 1 ]; then
import_hashicorp_gpg_key_noble
curl -sSL -o sentinel_checksums.txt ${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS
curl -sSL -o sentinel_checksums.txt.sig ${sentinel_releases_url}/${SENTINEL_VERSION}/sentinel_${SENTINEL_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig
if ! gpg --verify sentinel_checksums.txt.sig sentinel_checksums.txt; then
echo "Primary GPG verification failed, attempting fallback verification..."
verify_sentinel_sig
fi
# Verify the SHASUM matches the archive
shasum -a 256 --ignore-missing -c sentinel_checksums.txt
else
verify_sentinel_sig
# Verify the SHASUM matches the archive
shasum -a 256 --ignore-missing -c sentinel_checksums.txt
fi
else
echo "${SENTINEL_SHA256} *${SENTINEL_FILENAME}" >sentinel_checksums.txt
fi
Expand Down
18 changes: 18 additions & 0 deletions test/terraform/install_in_ubuntu_noble.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -e

# Import test library
source dev-container-features-test-lib

# Check to make sure the user is vscode
check "user is vscode" whoami | grep vscode

# Check if terraform was installed correctly
check "terraform installed" terraform --version

check "tflint" tflint --version

# Report results
reportResults

21 changes: 21 additions & 0 deletions test/terraform/install_in_ubuntu_noble_sentinel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

set -e

# Import test library for `check` command
source dev-container-features-test-lib

# Check to make sure the user is vscode
check "user is vscode" whoami | grep vscode

# Check if terraform was installed correctly
check "terraform installed" terraform --version

check "tflint" tflint --version

# Sentinel specific tests
check "sentinel" sentinel --version

# Report result
reportResults

16 changes: 16 additions & 0 deletions test/terraform/scenarios.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
{
"install_in_ubuntu_noble": {
"image": "mcr.microsoft.com/devcontainers/base:noble",
"features": {
"terraform": {
"version": "latest"
}
}
},
"install_in_ubuntu_noble_sentinel": {
"image": "mcr.microsoft.com/devcontainers/base:noble",
"features": {
"terraform": {
"installSentinel": true
}
}
},
"install_sentinel": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
Expand Down