diff --git a/.github/workflows/test-apply.yaml b/.github/workflows/test-apply.yaml index 4777381c..f7bb6735 100644 --- a/.github/workflows/test-apply.yaml +++ b/.github/workflows/test-apply.yaml @@ -1430,3 +1430,100 @@ jobs: uses: ./terraform-apply with: path: tests/workflows/test-apply/outputs + + ephemeral: + runs-on: ubuntu-24.04 + name: Apply a plan with ephemeral variables + permissions: + contents: read + pull-requests: write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Plan using default ephemeral value + uses: ./terraform-plan + with: + label: test-apply ephemeral + path: tests/workflows/test-apply/ephemeral + + - name: Apply using default ephemeral value + uses: ./terraform-apply + id: apply + with: + label: test-apply ephemeral + path: tests/workflows/test-apply/ephemeral + + - name: Verify outputs + env: + OUTPUT_STRING: ${{ steps.apply.outputs.v }} + run: | + if [[ "$OUTPUT_STRING" != "non-ephemeral" ]]; then + echo "::error:: output s not set correctly" + exit 1 + fi + + ## + + - name: Plan using explicit ephemeral value + uses: ./terraform-plan + with: + label: test-apply ephemeral 2 + path: tests/workflows/test-apply/ephemeral + variables: + region = "eu-west-1" + mv = "hello" + + - name: Apply using explicit ephemeral value + uses: ./terraform-apply + id: apply + with: + label: test-apply ephemeral 2 + path: tests/workflows/test-apply/ephemeral + variables: + region = "eu-west-1" + mv = "hello" + + - name: Verify outputs + env: + OUTPUT_STRING: ${{ steps.apply.outputs.v }} + run: | + if [[ "$OUTPUT_STRING" != "hello" ]]; then + echo "::error:: output s not set correctly" + exit 1 + fi + + ## + + - name: Plan using explicit non-ephemeral value + uses: ./terraform-plan + with: + label: test-apply ephemeral 3 + path: tests/workflows/test-apply/ephemeral + variables: + region = "eu-west-2" + mv = "goodbye" + + - name: Apply using mismatched explicit non-ephemeral value + uses: ./terraform-apply + continue-on-error: true + id: apply + with: + label: test-apply ephemeral 3 + path: tests/workflows/test-apply/ephemeral + variables: + region = "eu-west-2" + mv = "mismatch" + + - name: Check failed to apply + env: + OUTCOME: ${{ steps.apply.outcome }} + run: | + if [[ "$OUTCOME" != "failure" ]]; then + echo "Apply did not fail correctly" + exit 1 + fi diff --git a/image/actions.sh b/image/actions.sh index 35624948..677ceb74 100644 --- a/image/actions.sh +++ b/image/actions.sh @@ -360,6 +360,8 @@ function set-common-plan-args() { } function set-variable-args() { + VARIABLE_ARGS="" + if [[ -n "$INPUT_VAR_FILE" ]]; then for file in $(echo "$INPUT_VAR_FILE" | tr ',' '\n'); do @@ -368,25 +370,29 @@ function set-variable-args() { exit 1 fi - PLAN_ARGS="$PLAN_ARGS -var-file=$(relative_to "$INPUT_PATH" "$file")" + VARIABLE_ARGS="$VARIABLE_ARGS -var-file=$(relative_to "$INPUT_PATH" "$file")" done fi if [[ -n "$INPUT_VARIABLES" ]]; then echo "$INPUT_VARIABLES" >"$STEP_TMP_DIR/variables.tfvars" - PLAN_ARGS="$PLAN_ARGS -var-file=$STEP_TMP_DIR/variables.tfvars" + VARIABLE_ARGS="$VARIABLE_ARGS -var-file=$STEP_TMP_DIR/variables.tfvars" fi } -function set-plan-args() { - set-common-plan-args +function set-deprecated-var-args() { + DEPRECATED_VAR_ARGS="" if [[ -n "$INPUT_VAR" ]]; then for var in $(echo "$INPUT_VAR" | tr ',' '\n'); do - PLAN_ARGS="$PLAN_ARGS -var $var" + DEPRECATED_VAR_ARGS="$DEPRECATED_VAR_ARGS -var $var" done fi +} +function set-plan-args() { + set-common-plan-args + set-deprecated-var-args set-variable-args export PLAN_ARGS @@ -466,11 +472,11 @@ function plan() { fi # shellcheck disable=SC2086 - debug_log $TOOL_COMMAND_NAME plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT_ARG '$PLAN_ARGS' # don't expand PLAN_ARGS + debug_log $TOOL_COMMAND_NAME plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT_ARG $PLAN_ARGS $VARIABLE_ARGS '$DEPRECATED_VAR_ARGS' # don't expand deprecated var args set +e # shellcheck disable=SC2086 - (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT_ARG $PLAN_ARGS) \ + (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT_ARG $PLAN_ARGS $VARIABLE_ARGS $DEPRECATED_VAR_ARGS) \ 2>"$STEP_TMP_DIR/terraform_plan.stderr" \ | $TFMASK \ | tee /dev/fd/3 "$STEP_TMP_DIR/terraform_plan.stdout" \ @@ -494,11 +500,11 @@ function plan() { function destroy() { # shellcheck disable=SC2086 - debug_log $TOOL_COMMAND_NAME destroy -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS + debug_log $TOOL_COMMAND_NAME destroy -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS $VARIABLE_ARGS '$DEPRECATED_VAR_ARGS' # don't expand deprecated var args set +e # shellcheck disable=SC2086 - (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME destroy -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS) \ + (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME destroy -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS $VARIABLE_ARGS $DEPRECATED_VAR_ARGS) \ 2>"$STEP_TMP_DIR/terraform_destroy.stderr" \ | tee /dev/fd/3 \ >"$STEP_TMP_DIR/terraform_destroy.stdout" diff --git a/image/entrypoints/apply.sh b/image/entrypoints/apply.sh index 477486f6..6f6d5cf3 100755 --- a/image/entrypoints/apply.sh +++ b/image/entrypoints/apply.sh @@ -32,23 +32,33 @@ function apply() { set +e if [[ -n "$PLAN_OUT" ]]; then + # With Terrraform >= 1.10 Ephemeral variables must be specified again in the apply command. + # Non-ephemeral variables may be specified again, but may not be different from the plan. + # Terraform < 1.1.0 must not specify any variables when applying a saved plan. + + SAVED_PLAN_VARIABLES="" + if [[ "$TOOL_PRODUCT_NAME" == "Terraform" ]] && test-terraform-version ">=" "1.10.0"; then + SAVED_PLAN_VARIABLES="$VARIABLE_ARGS" + fi + # shellcheck disable=SC2086 - debug_log $TOOL_COMMAND_NAME apply -input=false -no-color -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT + debug_log $TOOL_COMMAND_NAME apply -input=false -no-color -lock-timeout=300s $PARALLEL_ARG $SAVED_PLAN_VARIABLES $PLAN_OUT # shellcheck disable=SC2086 - (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME apply -input=false -no-color -lock-timeout=300s $PARALLEL_ARG $PLAN_OUT) \ + (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME apply -input=false -no-color -lock-timeout=300s $PARALLEL_ARG $SAVED_PLAN_VARIABLES $PLAN_OUT) \ 2>"$STEP_TMP_DIR/terraform_apply.stderr" \ | $TFMASK \ | tee "$STEP_TMP_DIR/terraform_apply.stdout" APPLY_EXIT=${PIPESTATUS[0]} >&2 cat "$STEP_TMP_DIR/terraform_apply.stderr" + else # There is no plan file to apply, since the remote backend can't produce them. # Instead we need to do an auto approved apply using the arguments we would normally use for the plan + # shellcheck disable=SC2086,SC2016 + debug_log $TOOL_COMMAND_NAME apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS $VARIABLE_ARGS '$DEPRECATED_VAR_ARGS' # don't expand deprecated var args # shellcheck disable=SC2086 - debug_log $TOOL_COMMAND_NAME apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG '$PLAN_ARGS' # don't expand plan args - # shellcheck disable=SC2086 - (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS) \ + (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS $VARIABLE_ARGS $DEPRECATED_VAR_ARGS) \ 2>"$STEP_TMP_DIR/terraform_apply.stderr" \ | $TFMASK \ | tee "$STEP_TMP_DIR/terraform_apply.stdout" @@ -95,7 +105,7 @@ if [[ "$INPUT_PLAN_PATH" != "" ]]; then exit 1 fi - PLAN_OUT=$(realpath $INPUT_PLAN_PATH) + PLAN_OUT=$(realpath "$INPUT_PLAN_PATH") PLAN_EXIT=2 else plan diff --git a/image/entrypoints/test.sh b/image/entrypoints/test.sh index 1a58a8e3..565c2ae6 100755 --- a/image/entrypoints/test.sh +++ b/image/entrypoints/test.sh @@ -30,11 +30,11 @@ function set-test-args() { function test() { - debug_log $TOOL_COMMAND_NAME test -no-color $TEST_ARGS '$PLAN_ARGS' # don't expand PLAN_ARGS + debug_log $TOOL_COMMAND_NAME test -no-color $TEST_ARGS $VARIABLE_ARGS set +e # shellcheck disable=SC2086 - (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME test -no-color $TEST_ARGS $PLAN_ARGS) \ + (cd "$INPUT_PATH" && $TOOL_COMMAND_NAME test -no-color $TEST_ARGS $VARIABLE_ARGS) \ 2>"$STEP_TMP_DIR/terraform_test.stderr" \ | tee /dev/fd/3 \ >"$STEP_TMP_DIR/terraform_test.stdout" @@ -59,7 +59,6 @@ function test() { } set-test-args -PLAN_ARGS="" set-variable-args test diff --git a/tests/workflows/test-apply/ephemeral/main.tf b/tests/workflows/test-apply/ephemeral/main.tf new file mode 100644 index 00000000..d86fc327 --- /dev/null +++ b/tests/workflows/test-apply/ephemeral/main.tf @@ -0,0 +1,25 @@ +variable "region" { + type = string + default = "eu-west-2" +} + +variable "mv" { + type = string + default = "non-ephemeral" +} + +provider "aws" { + region = var.region +} + +output "v" { + value = var.mv +} + +resource "random_string" "add" { + length = 5 +} + +terraform { + required_version = ">=1.10" +}