Repository: gruntwork-io/terragrunt

Issue:
Quick Start should be updated
## Describe the enhancement

First time user of Terragrunt here. Thank you for working on this project and putting so much effort into it!

- [Step 3: Create a shared module](https://terragrunt.gruntwork.io/docs/getting-started/quick-start/#step-3-create-a-shared-module)

_Now, move the main.tf file from foo to shared:_

`mv foo/main.tf shared/main.tf`

This should be a copy, not a mv since foo/main.tf is needed afterwards

- [Step 4: Use Terragrunt to manage your units](https://terragrunt.gruntwork.io/docs/getting-started/quick-start/#step-4-use-terragrunt-to-manage-your-units)

_If you run terragrunt apply -auto-approve in the foo and bar directories, you’ll see that the content variable is set to the value you defined in the inputs block of the terragrunt.hcl file._ 

This example doesn't work as described because of the way Terragrunt deals with local_file, ${path.module} and .terragrunt-cache. Also it misses quite a lot of context for new users, especially that ${path.module} will create the new "hi.txt" files relative to .terragrunt-cache folder. To get this example working as described, I had to use get_terragrunt_dir() as well a new variable "output_dir" {} to get the files into the specific foo and bar folders as following:

shared/main.tf:

```
variable "content" {}
variable "output_dir" {}

resource "local_file" "file" {
  content  = var.content
  filename = "${var.output_dir}/hi.txt"
}
```

{foo,bar}/terragrunt.hcl

```
terraform {
  source = "../shared"
}

inputs = {
  output_dir = get_terragrunt_dir()
  content = "Hello from bar, Terragrunt!"
}
```

Cheers,
Daedalus

Discussion:
@yhakbar: Thanks for opening this issue, @davidatbob !

Would you be open to submitting a pull request to update the quick start to include the enhancements you're requesting?

Take note that any significant changes might require an update to [test/integration_docs_test.go](https://github.com/gruntwork-io/terragrunt/blob/main/test/integration_docs_test.go).

Solution patch:
diff --git a/.github/scripts/setup/generate-secrets.sh b/.github/scripts/setup/generate-secrets.sh
index 21ab5b31..5aac2adf 100755
--- a/.github/scripts/setup/generate-secrets.sh
+++ b/.github/scripts/setup/generate-secrets.sh
@@ -11,6 +11,7 @@ set -euo pipefail
 : "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is not set}"
 : "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is not set}"
 : "${AWS_TEST_S3_ASSUME_ROLE:?AWS_TEST_S3_ASSUME_ROLE is not set}"
+: "${AWS_TEST_OIDC_ROLE_ARN:?AWS_TEST_OIDC_ROLE_ARN is not set}"
 
 : "${GCLOUD_SERVICE_KEY:?GCLOUD_SERVICE_KEY is not set}"
 : "${GOOGLE_CLOUD_PROJECT:?GOOGLE_CLOUD_PROJECT is not set}"
@@ -51,6 +52,8 @@ for SECRET in $SECRETS; do
         printf "export AWS_SECRET_ACCESS_KEY='%s'\n" "${AWS_SECRET_ACCESS_KEY}" >> "$ENV_FILE"
     elif [[ "$SECRET" == "AWS_TEST_S3_ASSUME_ROLE" && -n "${AWS_TEST_S3_ASSUME_ROLE}" ]]; then
         printf "export AWS_TEST_S3_ASSUME_ROLE='%s'\n" "${AWS_TEST_S3_ASSUME_ROLE}" >> "$ENV_FILE"
+    elif [[ "$SECRET" == "AWS_TEST_OIDC_ROLE_ARN" && -n "${AWS_TEST_OIDC_ROLE_ARN}" ]]; then
+        printf "export AWS_TEST_OIDC_ROLE_ARN='%s'\n" "${AWS_TEST_OIDC_ROLE_ARN}" >> "$ENV_FILE"
     fi
 done
 
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
index 1f76fc51..2c19e451 100644
--- a/.github/workflows/integration-test.yml
+++ b/.github/workflows/integration-test.yml
@@ -171,6 +171,7 @@ jobs:
           GHA_DEPLOY_KEY: ${{ secrets.GHA_DEPLOY_KEY }}
           AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
           AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+          AWS_TEST_OIDC_ROLE_ARN: ${{ secrets.AWS_TEST_OIDC_ROLE_ARN }}
           GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }}
           GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
           GOOGLE_COMPUTE_ZONE: ${{ secrets.GOOGLE_COMPUTE_ZONE }}
@@ -241,4 +242,4 @@ jobs:
           report_paths: result.xml
           detailed_summary: 'true'
           include_time_in_summary: 'true'
-          group_suite: 'true'
\ No newline at end of file
+          group_suite: 'true'
diff --git a/.github/workflows/oidc-integration-test.yml b/.github/workflows/oidc-integration-test.yml
new file mode 100644
index 00000000..ae890dd6
--- /dev/null
+++ b/.github/workflows/oidc-integration-test.yml
@@ -0,0 +1,140 @@
+# These tests require different top-level permissions
+# than the other integration tests, so we're keeping them
+# in a separate workflow.
+
+name: OIDC Integration Tests
+
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+
+permissions:
+  id-token: write
+  contents: read
+  checks: write
+
+jobs:
+  test:
+    name: Test OIDC (${{ matrix.integration.name }})
+    runs-on: ${{ matrix.integration.os }}-latest
+    env:
+      MISE_PROFILE: cicd
+    strategy:
+      fail-fast: false
+      matrix:
+        integration:
+          - name: GHA AWS
+            os: ubuntu
+            target: ./...
+            tags: awsoidc
+            run: '^TestAws'
+            # We leave the key and secret on so that cleanup steps can use them.
+            secrets: [AWS_TEST_OIDC_ROLE_ARN, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY]
+            setup_scripts:
+              - .github/scripts/setup/tofu-switch.sh
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: "Save space on node"
+        if: runner.os != 'Windows'
+        run: |
+          sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
+          sudo docker image prune --all --force
+          sudo docker builder prune -a
+          df -h
+
+      # install dependencies for the integration tests
+      - name: Use mise to install dependencies
+        uses: jdx/mise-action@v2
+        with:
+          version: 2025.4.4
+          experimental: true
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Generate Secrets Environment
+        run: ./.github/scripts/setup/generate-secrets.sh
+        env:
+          NAME: ${{ matrix.integration.name }}
+          ENV_FILE: ${{ github.workspace }}/.env.secrets
+          SECRETS: ${{ join(matrix.integration.secrets, ' ') }}
+          GHA_DEPLOY_KEY: ${{ secrets.GHA_DEPLOY_KEY }}
+          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+          AWS_TEST_OIDC_ROLE_ARN: ${{ secrets.AWS_TEST_OIDC_ROLE_ARN }}
+          GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }}
+          GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
+          GOOGLE_COMPUTE_ZONE: ${{ secrets.GOOGLE_COMPUTE_ZONE }}
+          GOOGLE_IDENTITY_EMAIL: ${{ secrets.GOOGLE_IDENTITY_EMAIL }}
+          GOOGLE_PROJECT_ID: ${{ secrets.GOOGLE_PROJECT_ID }}
+          GCLOUD_SERVICE_KEY_IMPERSONATOR: ${{ secrets.GCLOUD_SERVICE_KEY_IMPERSONATOR }}
+          AWS_TEST_S3_ASSUME_ROLE: ${{ secrets.AWS_TEST_S3_ASSUME_ROLE }}
+        shell: bash
+
+      - name: Setup
+        if: runner.os != 'Windows'
+        run: ./.github/scripts/setup/run-setup-scripts.sh
+        shell: bash
+        env:
+          ENV_FILE: ${{ github.workspace }}/.env.secrets
+          SETUP_SCRIPTS: ${{ join(matrix.integration.setup_scripts, ' ') }}
+
+      - name: Windows Setup
+        if: runner.os == 'Windows'
+        run: pwsh -File ./.github/scripts/setup/windows-setup.ps1
+        shell: pwsh
+        env:
+          ENV_FILE: ${{ github.workspace }}/.env.secrets
+          SETUP_SCRIPTS: ${{ join(matrix.integration.setup_scripts, ' ') }}
+
+      - id: go-cache-paths
+        run: |
+          echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
+        shell: bash
+
+      - name: Go Build Cache
+        uses: actions/cache@v4
+        with:
+          path: ${{ steps.go-cache-paths.outputs.go-build }}
+          key: ${{ runner.os }}-go-integration-test-${{ matrix.integration.name }}-${{ hashFiles('**/go.sum') }}
+
+      - name: Run Tests
+        run: |
+          if [ "$SKIP" != "true" ]; then
+            source "${GITHUB_WORKSPACE}/.env.secrets"
+            # print command arguments
+            set -x
+            go test -v -timeout 45m ${TAGS:+-tags "$TAGS"} ${RUN:+-run "$RUN"} ${TEST_ARGS} "${TARGET}" | tee >(go-junit-report -set-exit-code > result.xml)
+          else
+            echo "Skipping tests for $NAME as the skip flag is true."
+          fi
+        shell: bash
+        env:
+          GITHUB_OAUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          TARGET: ${{ matrix.integration.target }}
+          TAGS: ${{ matrix.integration.tags }}
+          RUN: ${{ matrix.integration.run }}
+          SKIP: ${{ matrix.integration.skip }}
+          NAME: ${{ matrix.integration.name }}
+          TEST_ARGS: ${{ matrix.integration.test_args }}
+
+      - name: Upload Report (${{ matrix.integration.name }})
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-report-${{ matrix.integration.name }}
+          path: result.xml
+
+      - name: Display Test Results (${{ matrix.integration.name }})
+        uses: mikepenz/action-junit-report@v5
+        if: always()
+        with:
+          report_paths: result.xml
+          detailed_summary: 'true'
+          include_time_in_summary: 'true'
+          group_suite: 'true'
diff --git a/docs-starlight/src/content/docs/01-getting-started/01-quick-start.mdx b/docs-starlight/src/content/docs/01-getting-started/01-quick-start.mdx
index e646c84d..e6516b99 100644
--- a/docs-starlight/src/content/docs/01-getting-started/01-quick-start.mdx
+++ b/docs-starlight/src/content/docs/01-getting-started/01-quick-start.mdx
@@ -315,6 +315,42 @@ You can also see what to expect in your filesystem at each step [here](https://g
 
    This is where that additional verbosity in Terragrunt logging is really handy. You can see that Terragrunt concurrently ran `apply -auto-approve` in both the `foo` and `bar` units. The extra logging for Terragrunt also included information on the names of the units that were processed, and disambiguated the output from each unit.
 
+   When Terragrunt runs these commands, it creates a `.terragrunt-cache` directory in each unit's directory. This cache directory serves as Terragrunt's scratch directory where it:
+
+   - Downloads your remote OpenTofu/Terraform configurations
+   - Runs your OpenTofu/Terraform commands
+   - Stores downloaded modules and providers
+   - Stores generated files (in this case, the `hi.txt` file will be created under `.terragrunt-cache/[HASH]/[HASH]/hi.txt` rather than directly in the `foo` or `bar` directories)
+
+   With this configuration, the `hi.txt` files will be created directly in the `foo` and `bar` directories instead of the `.terragrunt-cache` directory. 
+
+   The `.terragrunt-cache` directory is typically added to `.gitignore` files, similar to the `.terraform` directory that OpenTofu generates. You can safely delete this folder at any time, and Terragrunt will recreate it as necessary.
+
+   If you want to control where the files are created, you can modify the module to accept an output directory parameter. For example, you can update the `shared/main.tf` file to:
+
+   ```hcl
+   variable "content" {}
+   variable "output_dir" {}
+
+   resource "local_file" "file" {
+     content  = var.content
+     filename = "${var.output_dir}/hi.txt"
+   }
+   ```
+
+   Then in your `foo/terragrunt.hcl` and `bar/terragrunt.hcl` files, you can use the `get_terragrunt_dir()` built-in function to get the directory where the `terragrunt.hcl` file is located:
+
+   ```hcl
+   terraform {
+     source = "../shared"
+   }
+
+   inputs = {
+     output_dir = get_terragrunt_dir()
+     content = "Hello from bar, Terragrunt!"
+   }
+   ```
+
    Similar to the `tofu` CLI, there is a prompt to confirm that you are sure you want to run the command in each unit when performing a command that's potentially destructive. You can skip this prompt by using the `--non-interactive` flag, just as you would with `-auto-approve` in OpenTofu.
 
    ```bash
diff --git a/docs/_docs/01_getting-started/01-quick-start.md b/docs/_docs/01_getting-started/01-quick-start.md
index 3f86a3da..6107c343 100644
--- a/docs/_docs/01_getting-started/01-quick-start.md
+++ b/docs/_docs/01_getting-started/01-quick-start.md
@@ -306,6 +306,42 @@ Are you sure you want to run 'terragrunt apply' in each folder of the stack desc
 
 This is where that additional verbosity in Terragrunt logging is really handy. You can see that Terragrunt concurrently ran `apply -auto-approve` in both the `foo` and `bar` units. The extra logging for Terragrunt also included information on the names of the units that were processed, and disambiguated the output from each unit.
 
+When Terragrunt runs these commands, it creates a `.terragrunt-cache` directory in each unit's directory. This cache directory serves as Terragrunt's scratch directory where it:
+
+- Downloads your remote OpenTofu/Terraform configurations
+- Runs your OpenTofu/Terraform commands
+- Stores downloaded modules and providers
+- Stores generated files (in this case, the `hi.txt` file will be created under `.terragrunt-cache/[HASH]/[HASH]/hi.txt` rather than directly in the `foo` or `bar` directories)
+
+With this configuration, the `hi.txt` files will be created directly in the `foo` and `bar` directories instead of the `.terragrunt-cache` directory.
+
+The `.terragrunt-cache` directory is typically added to `.gitignore` files, similar to the `.terraform` directory that OpenTofu generates.
+
+If you want to control where the files are created, you can modify the module to accept an output directory parameter. For example, you can update the `shared/main.tf` file to:
+
+```hcl
+variable "content" {}
+variable "output_dir" {}
+
+resource "local_file" "file" {
+  content  = var.content
+  filename = "${var.output_dir}/hi.txt"
+}
+```
+
+Then in your `foo/terragrunt.hcl` and `bar/terragrunt.hcl` files, you can use the `get_terragrunt_dir()` built-in function to get the directory where the `terragrunt.hcl` file is located:
+
+```hcl
+terraform {
+  source = "../shared"
+}
+
+inputs = {
+  output_dir = get_terragrunt_dir()
+  content = "Hello from bar, Terragrunt!"
+}
+```
+
 Similar to the `tofu` CLI, there is a prompt to confirm that you are sure you want to run the command in each unit when performing a command that's potentially destructive. You can skip this prompt by using the `--non-interactive` flag, just as you would with `-auto-approve` in OpenTofu.
 
 ```bash



Test patch:
diff --git a/test/fixtures/auth-provider-cmd/oidc/mock-auth-cmd.sh b/test/fixtures/auth-provider-cmd/oidc/mock-auth-cmd.sh
index 408b4841..e671449f 100755
--- a/test/fixtures/auth-provider-cmd/oidc/mock-auth-cmd.sh
+++ b/test/fixtures/auth-provider-cmd/oidc/mock-auth-cmd.sh
@@ -3,9 +3,9 @@
 set -o pipefail
 
 : "${AWS_TEST_OIDC_ROLE_ARN:?The AWS_TEST_OIDC_ROLE_ARN environment variable must be set.}"
-: "${CIRCLE_OIDC_TOKEN_V2:?The CIRCLE_OIDC_TOKEN_V2 environment variable must be set.}"
+: "${OIDC_TOKEN:?The OIDC_TOKEN environment variable must be set.}"
 
 jq -n \
 	--arg role "$AWS_TEST_OIDC_ROLE_ARN" \
-	--arg token "$CIRCLE_OIDC_TOKEN_V2" \
+	--arg token "$OIDC_TOKEN" \
 	'{awsRole: {roleARN: $role, webIdentityToken: $token}}'
diff --git a/test/fixtures/docs/01-quick-start/step-05.1/.gitignore b/test/fixtures/docs/01-quick-start/step-05.1/.gitignore
new file mode 100644
index 00000000..2ffc5504
--- /dev/null
+++ b/test/fixtures/docs/01-quick-start/step-05.1/.gitignore
@@ -0,0 +1,2 @@
+.terragrunt-cache
+hi.txt 
\ No newline at end of file
diff --git a/test/fixtures/docs/01-quick-start/step-05.1/README.md b/test/fixtures/docs/01-quick-start/step-05.1/README.md
new file mode 100644
index 00000000..f3c6669d
--- /dev/null
+++ b/test/fixtures/docs/01-quick-start/step-05.1/README.md
@@ -0,0 +1,3 @@
+This example demonstrates how to control output file locations using get_terragrunt_dir().
+
+The hi.txt files will be created directly in the foo and bar directories instead of the .terragrunt-cache directory. 
\ No newline at end of file
diff --git a/test/fixtures/docs/01-quick-start/step-05.1/bar/terragrunt.hcl b/test/fixtures/docs/01-quick-start/step-05.1/bar/terragrunt.hcl
new file mode 100644
index 00000000..0956eed4
--- /dev/null
+++ b/test/fixtures/docs/01-quick-start/step-05.1/bar/terragrunt.hcl
@@ -0,0 +1,8 @@
+terraform {
+  source = "../shared"
+}
+
+inputs = {
+  output_dir = get_terragrunt_dir()
+  content    = "Hello from bar, Terragrunt!"
+} 
\ No newline at end of file
diff --git a/test/fixtures/docs/01-quick-start/step-05.1/foo/terragrunt.hcl b/test/fixtures/docs/01-quick-start/step-05.1/foo/terragrunt.hcl
new file mode 100644
index 00000000..6db28e74
--- /dev/null
+++ b/test/fixtures/docs/01-quick-start/step-05.1/foo/terragrunt.hcl
@@ -0,0 +1,8 @@
+terraform {
+  source = "../shared"
+}
+
+inputs = {
+  output_dir = get_terragrunt_dir()
+  content    = "Hello from foo, Terragrunt!"
+} 
\ No newline at end of file
diff --git a/test/fixtures/docs/01-quick-start/step-05.1/shared/main.tf b/test/fixtures/docs/01-quick-start/step-05.1/shared/main.tf
new file mode 100644
index 00000000..bb1ea598
--- /dev/null
+++ b/test/fixtures/docs/01-quick-start/step-05.1/shared/main.tf
@@ -0,0 +1,7 @@
+variable "content" {}
+variable "output_dir" {}
+
+resource "local_file" "file" {
+  content  = var.content
+  filename = "${var.output_dir}/hi.txt"
+} 
\ No newline at end of file
diff --git a/test/fixtures/download/remote-relative/terragrunt.hcl b/test/fixtures/download/remote-relative/terragrunt.hcl
index a0b403f4..9388e978 100644
--- a/test/fixtures/download/remote-relative/terragrunt.hcl
+++ b/test/fixtures/download/remote-relative/terragrunt.hcl
@@ -3,5 +3,5 @@ inputs = {
 }
 
 terraform {
-  source = "github.com/gruntwork-io/terragrunt.git//test/fixtures/download/relative?ref=v0.77.22"
+  source = "github.com/gruntwork-io/terragrunt.git//test/fixtures/download/relative?ref=v0.79.0"
 }
diff --git a/test/integration_aws_oidc_test.go b/test/integration_aws_oidc_test.go
index b460663f..124ba7e1 100644
--- a/test/integration_aws_oidc_test.go
+++ b/test/integration_aws_oidc_test.go
@@ -9,7 +9,10 @@ package test_test
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
+	"io"
+	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
@@ -22,10 +25,24 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+const (
+	// This is the environment variable that GitHub Actions injects into the environment.
+	// We use this to detect if we are running in GitHub Actions.
+	githubActionsEnvVar = "GITHUB_ACTIONS"
+	// Environment variable from GitHub Actions containing the URL to request the OIDC token.
+	actionsIDTokenRequestURLEnvVar = "ACTIONS_ID_TOKEN_REQUEST_URL"
+	// Environment variable from GitHub Actions containing the bearer token to authenticate the OIDC token request.
+	actionsIDTokenRequestTokenEnvVar = "ACTIONS_ID_TOKEN_REQUEST_TOKEN"
+
+	// This is a fixture that tests the assume-role-web-identity-file flag.
+	testFixtureAssumeRoleWebIdentityFile = "fixtures/assume-role-web-identity/file-path"
+)
+
 func TestAwsAssumeRoleWebIdentityFile(t *testing.T) {
-	if os.Getenv("CIRCLECI") != "true" {
-		t.Skip("Skipping test because it requires valid CircleCI OIDC credentials to work")
-	}
+	// t.Parallel() cannot be used together with t.Setenv()
+	// t.Parallel()
+
+	token := fetchGitHubOIDCToken(t)
 
 	accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
 	secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
@@ -47,8 +64,6 @@ func TestAwsAssumeRoleWebIdentityFile(t *testing.T) {
 
 	role := os.Getenv("AWS_TEST_OIDC_ROLE_ARN")
 	require.NotEmpty(t, role)
-	token := os.Getenv("CIRCLE_OIDC_TOKEN_V2")
-	require.NotEmpty(t, token)
 
 	tokenFile := t.TempDir() + "/oidc-token"
 	require.NoError(t, os.WriteFile(tokenFile, []byte(token), 0400))
@@ -78,9 +93,10 @@ func TestAwsAssumeRoleWebIdentityFile(t *testing.T) {
 }
 
 func TestAwsAssumeRoleWebIdentityFlag(t *testing.T) {
-	if os.Getenv("CIRCLECI") != "true" {
-		t.Skip("Skipping test because it requires valid CircleCI OIDC credentials to work")
-	}
+	// t.Parallel() cannot be used together with t.Setenv()
+	// t.Parallel()
+
+	token := fetchGitHubOIDCToken(t)
 
 	// These tests need to be run without the static key + secret
 	// used by most AWS tests here.
@@ -99,8 +115,79 @@ func TestAwsAssumeRoleWebIdentityFlag(t *testing.T) {
 
 	roleARN := os.Getenv("AWS_TEST_OIDC_ROLE_ARN")
 	require.NotEmpty(t, roleARN)
-	token := os.Getenv("CIRCLE_OIDC_TOKEN_V2")
-	require.NotEmpty(t, token)
 
 	helpers.RunTerragrunt(t, "terragrunt apply --non-interactive --log-level trace --working-dir "+tmp+" --iam-assume-role "+roleARN+" --iam-assume-role-web-identity-token "+token)
 }
+
+func TestAwsReadTerragruntAuthProviderCmdWithOIDC(t *testing.T) {
+	// t.Parallel() cannot be used together with t.Setenv()
+	// t.Parallel()
+
+	token := fetchGitHubOIDCToken(t)
+
+	t.Setenv("OIDC_TOKEN", token)
+
+	cleanupTerraformFolder(t, testFixtureAuthProviderCmd)
+	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureAuthProviderCmd)
+	oidcPath := util.JoinPath(tmpEnvPath, testFixtureAuthProviderCmd, "oidc")
+	mockAuthCmd := filepath.Join(oidcPath, "mock-auth-cmd.sh")
+
+	helpers.RunTerragrunt(t, fmt.Sprintf(`terragrunt apply -auto-approve --non-interactive --working-dir %s --auth-provider-cmd %s`, oidcPath, mockAuthCmd))
+}
+
+// oidcTokenResponse defines the structure of the JSON response from GitHub's OIDC token endpoint.
+type oidcTokenResponse struct {
+	Value string `json:"value"`
+}
+
+// fetchGitHubOIDCToken retrieves the OIDC token from the GitHub Actions environment by calling the token request URL.
+// It skips the test if not running in a GitHub Actions environment or if the required environment variables are not set.
+// It uses t.Fatalf if any part of the token fetching process fails after the initial checks.
+func fetchGitHubOIDCToken(t *testing.T) string {
+	t.Helper()
+
+	if os.Getenv(githubActionsEnvVar) != "true" {
+		t.Skipf("Skipping test because it's not running in a GitHub Actions environment (expected %s=true)", githubActionsEnvVar)
+	}
+
+	requestURL := os.Getenv(actionsIDTokenRequestURLEnvVar)
+	if requestURL == "" {
+		t.Skipf("Skipping test: Environment variable %s must be set in GitHub Actions to fetch OIDC token.", actionsIDTokenRequestURLEnvVar)
+	}
+
+	requestToken := os.Getenv(actionsIDTokenRequestTokenEnvVar)
+	if requestToken == "" {
+		t.Skipf("Skipping test: Environment variable %s must be set in GitHub Actions to fetch OIDC token.", actionsIDTokenRequestTokenEnvVar)
+	}
+
+	client := &http.Client{}
+	postReqBody := strings.NewReader(`{"aud": "sts.amazonaws.com"}`)
+	req, err := http.NewRequest(http.MethodPost, requestURL, postReqBody)
+	require.NoError(t, err, "Failed to create OIDC token request to %s", requestURL)
+
+	req.Header.Set("Authorization", "Bearer "+requestToken)
+	req.Header.Set("Accept", "application/json")
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := client.Do(req)
+	require.NoError(t, err, "Failed to execute OIDC token request to %s", requestURL)
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		bodyBytes, readErr := io.ReadAll(resp.Body)
+		if readErr != nil {
+			t.Fatalf("OIDC token request to %s failed with status %s. Additionally, failed to read response body: %v", requestURL, resp.Status, readErr)
+		}
+		t.Fatalf("OIDC token request to %s failed with status %s. Response: %s", requestURL, resp.Status, string(bodyBytes))
+	}
+
+	body, err := io.ReadAll(resp.Body)
+	require.NoError(t, err, "Failed to read OIDC token response body from %s", requestURL)
+
+	var tokenResp oidcTokenResponse
+	err = json.Unmarshal(body, &tokenResp)
+	require.NoError(t, err, "Failed to unmarshal OIDC token response JSON from %s. Response: %s", requestURL, string(body))
+
+	require.NotEmpty(t, tokenResp.Value, "OIDC token 'value' field is empty in response from %s. Response: %s", requestURL, string(body))
+	return tokenResp.Value
+}
diff --git a/test/integration_aws_test.go b/test/integration_aws_test.go
index dd68bcad..4c48d5d6 100644
--- a/test/integration_aws_test.go
+++ b/test/integration_aws_test.go
@@ -44,7 +44,6 @@ const (
 	testFixtureS3Errors                          = "fixtures/s3-errors/"
 	testFixtureAssumeRole                        = "fixtures/assume-role/external-id"
 	testFixtureAssumeRoleDuration                = "fixtures/assume-role/duration"
-	testFixtureAssumeRoleWebIdentityFile         = "fixtures/assume-role-web-identity/file-path"
 	testFixtureReadIamRole                       = "fixtures/read-config/iam_role_in_file"
 	testFixtureOutputFromRemoteState             = "fixtures/output-from-remote-state"
 	testFixtureOutputFromDependency              = "fixtures/output-from-dependency"
@@ -1440,21 +1439,6 @@ func TestAwsReadTerragruntAuthProviderCmdWithSops(t *testing.T) {
 	assert.Equal(t, "Welcome to SOPS! Edit this file as you please!", outputs["hello"].Value)
 }
 
-func TestAwsReadTerragruntAuthProviderCmdWithOIDC(t *testing.T) {
-	t.Parallel()
-
-	if os.Getenv("CIRCLECI") != "true" {
-		t.Skip("Skipping test because it requires valid CircleCI OIDC credentials to work")
-	}
-
-	cleanupTerraformFolder(t, testFixtureAuthProviderCmd)
-	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureAuthProviderCmd)
-	oidcPath := util.JoinPath(tmpEnvPath, testFixtureAuthProviderCmd, "oidc")
-	mockAuthCmd := filepath.Join(oidcPath, "mock-auth-cmd.sh")
-
-	helpers.RunTerragrunt(t, fmt.Sprintf(`terragrunt apply -auto-approve --non-interactive --working-dir %s --auth-provider-cmd %s`, oidcPath, mockAuthCmd))
-}
-
 func TestAwsReadTerragruntConfigIamRole(t *testing.T) {
 	t.Parallel()
 
diff --git a/test/integration_download_test.go b/test/integration_download_test.go
index 9bf89d95..a0ba1eb2 100644
--- a/test/integration_download_test.go
+++ b/test/integration_download_test.go
@@ -198,8 +198,6 @@ func TestInvalidRemoteDownloadWithRetries(t *testing.T) {
 }
 
 func TestRemoteDownloadWithRelativePath(t *testing.T) {
-	t.Skip("This test needs to be skipped for now, as there's a recursive reference happening on tagged modules that needs to be plumbed out.")
-
 	t.Parallel()
 
 	helpers.CleanupTerraformFolder(t, testFixtureRemoteRelativeDownloadPath)
diff --git a/test/integration_test.go b/test/integration_test.go
index 32083325..8d7a432e 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -3165,9 +3165,6 @@ func TestNoMultipleInitsWithoutSourceChange(t *testing.T) {
 }
 
 func TestAutoInitWhenSourceIsChanged(t *testing.T) {
-	// TODO: Resolve this.
-	t.Skip("This test needs to be skipped for now, as there's recursive references happening on tagged modules that needs to be plumbed out.")
-
 	t.Parallel()
 
 	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureDownload)
@@ -3179,7 +3176,7 @@ func TestAutoInitWhenSourceIsChanged(t *testing.T) {
 	if err != nil {
 		require.NoError(t, err)
 	}
-	updatedHcl := strings.ReplaceAll(contents, "__TAG_VALUE__", "v0.77.21")
+	updatedHcl := strings.ReplaceAll(contents, "__TAG_VALUE__", "v0.78.4")
 	require.NoError(t, os.WriteFile(terragruntHcl, []byte(updatedHcl), 0444))
 
 	stdout := bytes.Buffer{}
@@ -3190,7 +3187,7 @@ func TestAutoInitWhenSourceIsChanged(t *testing.T) {
 	// providers initialization during first plan
 	assert.Equal(t, 1, strings.Count(stdout.String(), "has been successfully initialized!"))
 
-	updatedHcl = strings.ReplaceAll(contents, "__TAG_VALUE__", "v0.77.22")
+	updatedHcl = strings.ReplaceAll(contents, "__TAG_VALUE__", "v0.79.0")
 	require.NoError(t, os.WriteFile(terragruntHcl, []byte(updatedHcl), 0444))
 
 	stdout = bytes.Buffer{}