Repository: warrensbox/terraform-switcher (go)

Issue:
[Feature request] Environment variable for non-standard install location
### Discussed in https://github.com/warrensbox/terraform-switcher/discussions/561

<div type='discussions-op-text'>

<sup>Originally posted by **dbopus2** March 10, 2025</sup>
Would you consider adding an environment variable for non-standard install location? Same functionality as the -i and --install cli options.</div>


Discussion:
Also:
* Update https://tfswitch.warrensbox.com/usage/commandline/ accordingly
* Update https://tfswitch.warrensbox.com/usage/config-files/ accordingly
* Consider adding TOML config key for consistency
* Add tests for both env var and config key
* Consider implementing the same for `-b, --bin=value` arg
* Side task: add `arch` and `log-level` TOML keys description to https://tfswitch.warrensbox.com/usage/config-files/
This partially overlaps with https://github.com/warrensbox/terraform-switcher/pull/559, so I'll need to have that other PR merged first before I can start looking into this feature request 🤷🏻 
I'd actually like to see the -i option added to the TOML file as well.

Had a situation come up at work the other day where this would come in very handy. On a shared server with a small C drive, if we have a bunch of users that work with multiple different terraform versions it can start to add up.

Having to pass -i to tfswitch all the time is a pain so being able to default the alternate location in the TOML file would be great.
@mj790 Thanks for describing your use case. Usual "thumbs up" to the first message would allow us to easily prioritize issues 👍🏻 I'll work on this request once that other PR is reviewed and approved by @warrensbox and @MatthewJohn. Stay tuned 🎶 

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,9 @@
 name: Run tests
+permissions: {} # yamllint disable-line rule:braces
 
 on:
   push:
-    branches: 
+    branches:
       - '*'       # matches every branch that doesn't contain a '/'
       - '*/*'     # matches every branch containing a single '/'
       - '**'      # matches every branch
@@ -21,7 +22,8 @@ jobs:
       - name: Install Go
         uses: actions/setup-go@v5
         with:
-          go-version: '1.23'
+          go-version-file: 'go.mod'
+          cache: false
 
       - name: Check vet
         run: |
@@ -42,7 +44,6 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest]
-        go_version: ['1.23']
     runs-on: ${{ matrix.os }}
     steps:
 
@@ -52,7 +53,8 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@v5
       with:
-        go-version: ${{ matrix.go_version }}
+        go-version-file: 'go.mod'
+        cache: false
 
     - name: Build code
       run: go build -v ./...
@@ -81,7 +83,6 @@ jobs:
     strategy:
       matrix:
         os: [windows-latest]
-        go_version: ['1.23']
     runs-on: ${{ matrix.os }}
     steps:
 
@@ -91,7 +92,8 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@v5
       with:
-        go-version: ${{ matrix.go_version }}
+        go-version-file: 'go.mod'
+        cache: false
 
     - name: Running unit tests
       run: |
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -10,6 +10,7 @@
 # supported CodeQL languages.
 #
 name: "CodeQL"
+permissions: {} # yamllint disable-line rule:braces
 
 on:
   push:
@@ -64,7 +65,8 @@ jobs:
       - name: Set up Go
         uses: actions/setup-go@v5
         with:
-          go-version: '1.23'
+          go-version-file: 'go.mod'
+          cache: false
 
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,4 +1,5 @@
 name: Release (Manual Step)
+permissions: {} # yamllint disable-line rule:braces
 
 on:
   workflow_dispatch:
@@ -10,8 +11,10 @@ on:
 jobs:
   tfswitch-release:
     runs-on: ubuntu-latest
-    steps:
+    permissions:
+      contents: write
 
+    steps:
     - name: Check if provided input is valid
       run: |
         echo "Semantic Version: ${{ github.event.inputs.name }}"
@@ -32,7 +35,8 @@ jobs:
     - name: Install Go
       uses: actions/setup-go@v5
       with:
-        go-version: '1.23' # The Go version to download (if necessary) and use
+        go-version-file: 'go.mod'
+        cache: false
 
     # Double check Go version
     - name: Go version
@@ -64,7 +68,7 @@ jobs:
         VERBOSE: false
 
     # Echo version
-    - name: Echo version 
+    - name: Echo version
       run: |
         echo ${{ steps.semver-tag-dry.outputs.tag }}
 
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ GOARCH    ?= $(shell $(GOBINARY) env GOARCH)
 
 $(EXE): version go.mod *.go lib/*.go
 	mkdir -p "$(BUILDPATH)/"
-	$(GOBINARY) build -v -ldflags "-X main.version=$(VER)" -o "$(BUILDPATH)/$@" $(PKG)
+	$(GOBINARY) build -v -ldflags "-X 'main.version=$(VER)'" -o "$(BUILDPATH)/$@" $(PKG)
 
 .PHONY: release
 release: $(EXE) darwin linux windows
diff --git a/lib/install.go b/lib/install.go
--- a/lib/install.go
+++ b/lib/install.go
@@ -80,7 +80,7 @@ func install(product Product, tfversion, binPath, installPath, mirrorURL, goarch
 	}
 
 	if goarch != runtime.GOARCH {
-		logger.Warnf("Installing for %q architecture on %q!", goarch, runtime.GOARCH)
+		logger.Warnf("Installing for %q CPU architecture on %q!", goarch, runtime.GOARCH)
 	}
 
 	goos := runtime.GOOS
diff --git a/lib/param_parsing/environment.go b/lib/param_parsing/environment.go
--- a/lib/param_parsing/environment.go
+++ b/lib/param_parsing/environment.go
@@ -1,23 +1,43 @@
 package param_parsing
 
-import "os"
+import (
+	"os"
+	"reflect"
+)
 
 func GetParamsFromEnvironment(params Params) Params {
-	if envArch := os.Getenv("TF_ARCH"); envArch != "" {
-		params.Arch = envArch
-		logger.Debugf("Using architecture from environment variable \"TF_ARCH\": %q", envArch)
-	}
-	if envVersion := os.Getenv("TF_VERSION"); envVersion != "" {
-		params.Version = envVersion
-		logger.Debugf("Using version from environment variable \"TF_VERSION\": %q", envVersion)
-	}
-	if envDefaultVersion := os.Getenv("TF_DEFAULT_VERSION"); envDefaultVersion != "" {
-		params.DefaultVersion = envDefaultVersion
-		logger.Debugf("Using default-version from environment variable \"TF_DEFAULT_VERSION\": %q", envDefaultVersion)
-	}
-	if envProduct := os.Getenv("TF_PRODUCT"); envProduct != "" {
-		params.Product = envProduct
-		logger.Debugf("Using product from environment variable \"TF_PRODUCT\": %q", envProduct)
+	reflectedParams := reflect.ValueOf(&params)
+	for _, envVar := range paramMappings {
+		description := envVar.description
+		env := envVar.env
+		param := envVar.param
+		toml := envVar.toml
+
+		if len(env) == 0 {
+			logger.Errorf("Internal error: environment variable name is empty for parameter %q mapping, skipping assignment", param)
+			continue
+		}
+		if len(param) == 0 {
+			logger.Errorf("Internal error: parameter name is empty for environment variable %q mapping, skipping assignment", env)
+			continue
+		}
+		if len(description) == 0 {
+			description = param
+		}
+
+		paramKey := reflect.Indirect(reflectedParams).FieldByName(param)
+		if paramKey.Kind() != reflect.String {
+			logger.Warnf("Parameter %q is not a string, skipping assignment from environment variable %q", param, env)
+			continue
+		}
+
+		if envVarValue := os.Getenv(env); envVarValue != "" {
+			logger.Debugf("%s (%q) from environment variable %q: %q", description, toml, env, envVarValue)
+			if !paramKey.CanSet() {
+				logger.Warnf("Parameter %q cannot be set, skipping assignment from environment variable %q", param, env)
+			}
+			paramKey.SetString(envVarValue)
+		}
 	}
 	return params
 }
diff --git a/lib/param_parsing/parameters.go b/lib/param_parsing/parameters.go
--- a/lib/param_parsing/parameters.go
+++ b/lib/param_parsing/parameters.go
@@ -35,6 +35,24 @@ type Params struct {
 	VersionFlag      bool
 }
 
+type paramMapping struct {
+	description string
+	env         string
+	param       string
+	toml        string
+}
+
+// This is used to automatically instate Environment variables and TOML keys
+var paramMappings = []paramMapping{
+	{param: "Arch", env: "TF_ARCH", toml: "arch", description: "CPU architecture"},
+	{param: "CustomBinaryPath", env: "TF_BINARY_PATH", toml: "bin", description: "Custom binary path"},
+	{param: "DefaultVersion", env: "TF_DEFAULT_VERSION", toml: "default-version", description: "Default version"},
+	{param: "InstallPath", env: "TF_INSTALL_PATH", toml: "install", description: "Custom install path"},
+	{param: "LogLevel", env: "TF_LOG_LEVEL", toml: "log-level", description: "Log level"},
+	{param: "Product", env: "TF_PRODUCT", toml: "product", description: "Product"},
+	{param: "Version", env: "TF_VERSION", toml: "version", description: "Version"},
+}
+
 var logger *slog.Logger
 
 func GetParameters() Params {
@@ -163,22 +181,23 @@ func populateParams(params Params) Params {
 	}
 
 	if isShortRun {
-		logger.Debugf("Resolved CPU architecture: %q", params.Arch)
 		if params.DryRun {
 			logger.Info("[DRY-RUN] No changes will be made")
 		} else {
 			logger.Debugf("Resolved dry-run: %t", params.DryRun)
 		}
+
+		logger.Debugf("Resolved CPU architecture: %q", params.Arch)
 		if params.DefaultVersion != "" {
 			logger.Debugf("Resolved fallback version: %q", params.DefaultVersion)
 		}
-		logger.Debugf("Resolved installation path: %q", filepath.Join(params.InstallPath, lib.InstallDir))
-		logger.Debugf("Resolved installation target: %q", params.CustomBinaryPath)
-		logger.Debugf("Resolved installation version: %q", params.Version)
+		logger.Debugf("Resolved binary path: %q", params.CustomBinaryPath)
+		logger.Debugf("Resolved install path: %q", filepath.Join(params.InstallPath, lib.InstallDir))
+		logger.Debugf("Resolved install version: %q", params.Version)
 		logger.Debugf("Resolved log level: %q", params.LogLevel)
 		logger.Debugf("Resolved mirror URL: %q", params.MirrorURL)
 		logger.Debugf("Resolved product name: %q", params.Product)
-		logger.Debugf("Resolved target directory: %q", params.ChDirPath)
+		logger.Debugf("Resolved working directory: %q", params.ChDirPath)
 	}
 
 	return params
diff --git a/lib/param_parsing/toml.go b/lib/param_parsing/toml.go
--- a/lib/param_parsing/toml.go
+++ b/lib/param_parsing/toml.go
@@ -1,8 +1,8 @@
 package param_parsing
 
 import (
-	"os"
 	"path/filepath"
+	"reflect"
 
 	"github.com/spf13/viper"
 	"github.com/warrensbox/terraform-switcher/lib"
@@ -27,29 +27,37 @@ func getParamsTOML(params Params) (Params, error) {
 			return params, errs
 		}
 
-		if configKey := "arch"; viperParser.Get(configKey) != nil {
-			params.Arch = os.ExpandEnv(viperParser.GetString(configKey))
-			logger.Debugf("OS architecture (%q) from %q: %q", configKey, tomlPath, params.Arch)
-		}
-		if configKey := "bin"; viperParser.Get(configKey) != nil {
-			params.CustomBinaryPath = os.ExpandEnv(viperParser.GetString(configKey))
-			logger.Debugf("Installation target (%q) from %q: %q", configKey, tomlPath, params.CustomBinaryPath)
-		}
-		if configKey := "log-level"; viperParser.Get(configKey) != nil {
-			params.LogLevel = viperParser.GetString(configKey)
-			logger.Debugf("Logging level (%q) from %q: %q", configKey, tomlPath, params.LogLevel)
-		}
-		if configKey := "version"; viperParser.Get(configKey) != nil {
-			params.Version = viperParser.GetString(configKey)
-			logger.Debugf("Installation version (%q) from %q: %q", configKey, tomlPath, params.Version)
-		}
-		if configKey := "default-version"; viperParser.Get(configKey) != nil {
-			params.DefaultVersion = viperParser.GetString(configKey)
-			logger.Debugf("Fallback version (%q) from %q: %q", configKey, tomlPath, params.DefaultVersion)
-		}
-		if configKey := "product"; viperParser.Get(configKey) != nil {
-			params.Product = viperParser.GetString(configKey)
-			logger.Debugf("Product name (%q) from %q: %q", configKey, tomlPath, params.Product)
+		reflectedParams := reflect.ValueOf(&params)
+		for _, configKey := range paramMappings {
+			description := configKey.description
+			param := configKey.param
+			toml := configKey.toml
+
+			if len(toml) == 0 {
+				logger.Errorf("Internal error: TOML key name is empty for parameter %q mapping, skipping assignment", param)
+				continue
+			}
+			if len(param) == 0 {
+				logger.Errorf("Internal error: parameter name is empty for TOML key %q mapping, skipping assignment", toml)
+				continue
+			}
+			if len(description) == 0 {
+				description = param
+			}
+
+			paramKey := reflect.Indirect(reflectedParams).FieldByName(param)
+			if paramKey.Kind() != reflect.String {
+				logger.Warnf("Parameter %q is not a string, skipping assignment from TOML key %q", param, toml)
+				continue
+			}
+			if viperParser.Get(toml) != nil {
+				configKeyValue := viperParser.GetString(toml)
+				logger.Debugf("%s (%q) from %q: %q", description, toml, tomlPath, configKeyValue)
+				if !paramKey.CanSet() {
+					logger.Warnf("Parameter %q cannot be set, skipping assignment from TOML key %q", param, toml)
+				}
+				paramKey.SetString(configKeyValue)
+			}
 		}
 	}
 	return params, nil
diff --git a/www/docs/usage/commandline.md b/www/docs/usage/commandline.md
--- a/www/docs/usage/commandline.md
+++ b/www/docs/usage/commandline.md
@@ -32,7 +32,7 @@ You can also set environment variables for tfswitch to override some configurati
 For example:
 
 ```bash
-export TF_VERSION=0.14.4
+export TF_VERSION="0.14.4"
 tfswitch # Will automatically switch to terraform version 0.14.4
 ```
 
@@ -43,7 +43,7 @@ tfswitch # Will automatically switch to terraform version 0.14.4
 For example:
 
 ```bash
-export TF_DEFAULT_VERSION=0.14.4
+export TF_DEFAULT_VERSION="0.14.4"
 tfswitch # Will automatically switch to terraform version 0.14.4
 ```
 
@@ -59,13 +59,32 @@ This can either be set to:
 For example:
 
 ```bash
-export TF_PRODUCT=opentofu
+export TF_PRODUCT="opentofu"
 tfswitch # Will install opentofu instead of terraform
 ```
 
+### `TF_LOG_LEVEL`
+
+`TF_LOG_LEVEL` environment variable can be set to override default log level.
+
+- Supported log levels:
+  - `ERROR`:  includes `PANIC`, `FATAL`, `ERROR`
+  - `INFO`:   includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `INFO` (default)
+  - `NOTICE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`
+  - `DEBUG`:  includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`
+  - `TRACE`:  includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`, `TRACE`
+  - Any other log level value falls under default logging level
+
+For example:
+
+```bash
+export TF_LOG_LEVEL="DEBUG"
+tfswitch # Will output debug logs
+```
+
 ### `TF_ARCH`
 
-`TF_ARCH` environment variable can be set to override default CPU architecture for downloaded Terraform binary.
+`TF_ARCH` environment variable can be set to override default CPU architecture of downloaded binaries.
 
 - This can be set to any string, though incorrect values will result in download failure.
 - Suggested values: `amd64`, `arm64`, `386`.
@@ -76,8 +95,32 @@ tfswitch # Will install opentofu instead of terraform
 For example:
 
 ```bash
-export TF_ARCH=amd64
-tfswitch # Will install Terraform binary for amd64 architecture
+export TF_ARCH="amd64"
+tfswitch # Will install binary for amd64 architecture
+```
+
+### `TF_BINARY_PATH`
+
+`tfswitch` defaults to install to the `/usr/local/bin/` directory (and falls back to `$HOME/bin/` otherwise). The target filename is resolved automatically based on the `product` parameter.  
+`TF_BINARY_PATH` environment variable can be set to specify a **full installation path** (directory + file name). If target directory does not exist, `tfswitch` falls back to `$HOME/bin/` directory.
+
+For example:
+
+```bash
+export TF_BINARY_PATH="$HOME/bin/terraform" # Path to the file
+tfswitch # Will install binary as $HOME/bin/terraform
+```
+
+### `TF_INSTALL_PATH`
+
+`tfswitch` defaults to download binaries to the `$HOME/.terraform.versions/` directory.  
+`TF_INSTALL_PATH` environment variable can be set to specify the parent directory for `.terraform.versions` directory. Current user must have write permissions to the target directory. If the target directory does not exist, `tfswitch` will create it.
+
+For example:
+
+```bash
+export TF_INSTALL_PATH="/var/cache" # Path to the directory where `.terraform.versions` directory resides
+tfswitch # Will download actual binary to /var/cache/.terraform.versions/
 ```
 
 ## Install latest version only
diff --git a/www/docs/usage/config-files.md b/www/docs/usage/config-files.md
--- a/www/docs/usage/config-files.md
+++ b/www/docs/usage/config-files.md
@@ -9,8 +9,9 @@ tfswitch -c terraform_dir
 
 ## Use `version.tf` file
 
-If a .tf file with the terraform constraints is included in the current directory, it should automatically download or switch to that terraform version.  
-For example, the following should automatically switch terraform to the lastest version:  
+If a `.tf` file with the version constraints is included in the current directory, `tfswitch` should automatically download or switch to that terraform version.  
+Also please refer to [Order of Terraform version definition precedence](general.md) page for more information on how `tfswitch` determines the version to use.  
+For example, the following should automatically switch to the latest available version newer than `0.12.8`:  
 
 ```hcl
 terraform {
@@ -30,21 +31,23 @@ terraform {
 ![tfswitchrc](../static/tfswitch-v6.gif)
 
 1. Create a `.tfswitchrc` file containing the desired version
-2. For example, `echo "0.10.5" >> .tfswitchrc` for version 0.10.5 of terraform
-3. Run the command `tfswitch` in the same directory as your `.tfswitchrc`
+2. For example, `echo "0.10.5" >> .tfswitchrc` for version `0.10.5`
+3. Run the command `tfswitch` in the same directory as this `.tfswitchrc` file
 
 *Instead of a `.tfswitchrc` file, a `.terraform-version` file may be used for compatibility with [`tfenv`](https://github.com/tfutils/tfenv#terraform-version-file) and other tools which use it*
 
 ## Use `.tfswitch.toml` file  (For non-admin users with limited privilege on their computers)
 
-This is similiar to using a `.tfswitchrc` file, but you can specify a custom binary path for your terraform installation
+`tfswitch` defaults to install to the `/usr/local/bin/` directory (and falls back to `$HOME/bin/` otherwise). The target filename is resolved automatically based on the `product` attribute ([see below](#setting-product-using-tfswitchtoml-file)). If you do not have write access to `/usr/local/bin/` directory, you can use the `.tfswitch.toml` file to specify a **full installation path** (directory + file name).  
+This is similar to using a `.tfswitchrc` file, but you specify a custom binary path for the installation:
 
 ![toml1](../static/tfswitch-v7.gif)
 ![toml2](../static/tfswitch-v8.gif)
 
-1. Create a custom binary path. Ex: `mkdir $HOME/bin`
-2. Add the path to your PATH. Ex: `export PATH=$PATH:$HOME/bin` (add this to your Bash profile or Zsh profile)
-3. Pass `-b` or `--bin` parameter with your custom path to install Terraform. Ex: `tfswitch -b $HOME/bin/terraform 0.10.8`
+1. Create a directory for the custom binary path. Ex: `mkdir -p "$HOME/bin/"`
+2. Add the path to the directory to your `PATH` environment variable. Ex: `export PATH="$PATH:$HOME/bin"` (add this to your Bash profile or Zsh profile)
+3. Pass `-b` or `--bin` parameter with the custom binary path as value (this must be a first level pointer inside the directory from above). Ex: `tfswitch -b "$HOME/bin/terraform" 0.10.8`
+   - If target directory for custom binary path does not exist, `tfswitch` falls back to `$HOME/bin/` directory
 4. Optionally, you can create a `.tfswitch.toml` file in your home directory (`~/.tfswitch.toml`)
 5. Your `~/.tfswitch.toml` file should look like this:
 
@@ -53,20 +56,18 @@ bin = "$HOME/bin/terraform"
 version = "0.11.3"
 ```
 
-6. Run `tfswitch` and it should automatically install the required terraform version in the specified binary path
+6. Run `tfswitch` and it should automatically install the required version in the specified binary path
 
-**NOTE**
-
-1. For Linux users that do not have write permission to `/usr/local/bin/`, `tfswitch` will attempt to install terraform at `$HOME/bin`. Run `export PATH=$PATH:$HOME/bin` to append bin to PATH
-2. For Windows host, `tfswitch` need to be run under `Administrator` mode, and `$HOME/.tfswitch.toml` with `bin` must be defined (with a valid path) as minimum, below is an example for `$HOME/.tfswitch.toml` on windows
+Below is an example for `$HOME/.tfswitch.toml` on Windows:
 
 ```toml
 bin = "C:\\Users\\<%USRNAME%>\\bin\\terraform.exe"
 ```
 
-## Setting the default version using `.tfswitch.toml` file
+## Setting the default (fallback) version using `.tfswitch.toml` file
 
-The `.tfswitch.toml` file can be configured with a `default-version` attribute to configure tfswitch a particular version, if no other sources of versions are found
+By default, if `tfswsitch` is unable to determine the version to use, it errors out.  
+The `.tfswitch.toml` file can be configured with a `default-version` attribute for `tfswitch` to use a particular version, if no other sources of versions are found
 
 ```toml
 default-version = "1.5.4"
@@ -74,7 +75,8 @@ default-version = "1.5.4"
 
 ## Setting product using `.tfswitch.toml` file
 
-The `.tfswitch.toml` file can be configured with a `product` attribute to configure tfswitch to use Terraform or OpenTofu, by default:
+`tfswitch` defaults to install Terraform binaries.  
+The `.tfswitch.toml` file can be configured with a `product` attribute for `tfswitch` to use either Terraform or OpenTofu by default:
 
 ```toml
 product = "opentofu"
@@ -86,10 +88,55 @@ or
 product = "terraform"
 ```
 
+## Setting log level using `.tfswitch.toml` file
+
+`tfswitch` defaults to `INFO` log level.  
+The `.tfswitch.toml` file can be configured with a `log-level` attribute for `tfswitch` to use non-default logging verbosity:
+
+```toml
+log-level = "INFO"
+```
+
+- Supported log levels:
+  - `ERROR`:  includes `PANIC`, `FATAL`, `ERROR`
+  - `INFO`:   includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `INFO` (default)
+  - `NOTICE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`
+  - `DEBUG`:  includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`
+  - `TRACE`:  includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`, `TRACE`
+  - Any other log level value falls under default logging level
+
+## Overriding CPU architecture type for the downloaded binary using `.tfswitch.toml` file
+
+CPU architecture of the downloaded binaries defaults to `tfswitch`'s host architecture.  
+The `.tfswitch.toml` file can be configured with a `arch` attribute for `tfswitch` to download binary of non-default architecture type:
+
+```toml
+arch = "arm64"
+```
+
+- This can be set to any string, though incorrect values will result in download failure.
+- Suggested values: `amd64`, `arm64`, `386`.
+- Check available Arch types at:
+  - [Terraform Downloads](https://releases.hashicorp.com/terraform/)
+  - [OpenTofu Downloads](https://get.opentofu.org/tofu/)
+
+## Overriding installation directory, where actual binaries are stored, using `.tfswitch.toml` file
+
+`tfswitch` defaults to download binaries to the `$HOME/.terraform.versions/` directory.  
+The `.tfswitch.toml` file can be configured with a `install` attribute to specify the parent directory for `.terraform.versions` directory.
+
+```toml
+install = "/var/cache"
+```
+
+**NOTE**:
+- Current user must have write permissions to the target directory
+- If the target directory does not exist, `tfswitch` will create it
+
 ## Use `terragrunt.hcl` file
 
-If a terragrunt.hcl file with the terraform constraint is included in the current directory, it should automatically download or switch to that terraform version. 
-For example, the following should automatically switch terraform to the lastest version 0.13:
+If a terragrunt.hcl file with the terraform constraint is included in the current directory, it should automatically download or switch to that terraform version.  
+For example, the following should automatically switch Terraform to the latest version 0.13:
 
 ```hcl
 terragrunt_version_constraint = ">= 0.26, < 0.27"


Test patch:
diff --git a/lib/param_parsing/environment_test.go b/lib/param_parsing/environment_test.go
--- a/lib/param_parsing/environment_test.go
+++ b/lib/param_parsing/environment_test.go
@@ -54,6 +54,45 @@ func TestGetParamsFromEnvironment_product_from_env(t *testing.T) {
 	params = GetParamsFromEnvironment(params)
 	_ = os.Unsetenv("TF_PRODUCT")
 	if params.Product != expected {
-		t.Error("Determined version is not matching. Got " + params.Product + ", expected " + expected)
+		t.Error("Determined product is not matching. Got " + params.Product + ", expected " + expected)
+	}
+}
+
+func TestGetParamsFromEnvironment_bin_from_env(t *testing.T) {
+	logger = lib.InitLogger("DEBUG")
+	var params Params
+	expected := "custom_binary_path_from_env"
+	_ = os.Setenv("TF_BINARY_PATH", expected)
+	params = initParams(params)
+	params = GetParamsFromEnvironment(params)
+	_ = os.Unsetenv("TF_BINARY_PATH")
+	if params.CustomBinaryPath != expected {
+		t.Errorf("Determined custom binary path is not matching. Got %q, expected %q", params.CustomBinaryPath, expected)
+	}
+}
+
+func TestGetParamsFromEnvironment_install_from_env(t *testing.T) {
+	logger = lib.InitLogger("DEBUG")
+	var params Params
+	expected := "/custom_install_path_from_env"
+	_ = os.Setenv("TF_INSTALL_PATH", expected)
+	params = initParams(params)
+	params = GetParamsFromEnvironment(params)
+	_ = os.Unsetenv("TF_INSTALL_PATH")
+	if params.InstallPath != expected {
+		t.Errorf("Determined custom install path is not matching. Got %q, expected %q", params.InstallPath, expected)
+	}
+}
+
+func TestGetParamsFromEnvironment_log_level_from_env(t *testing.T) {
+	logger = lib.InitLogger("DEBUG")
+	var params Params
+	expected := "DEBUG"
+	_ = os.Setenv("TF_LOG_LEVEL", expected)
+	params = initParams(params)
+	params = GetParamsFromEnvironment(params)
+	_ = os.Unsetenv("TF_LOG_LEVEL")
+	if params.LogLevel != expected {
+		t.Errorf("Determined log level is not matching. Got %q, expected %q", params.LogLevel, expected)
 	}
 }
diff --git a/lib/param_parsing/toml_test.go b/lib/param_parsing/toml_test.go
--- a/lib/param_parsing/toml_test.go
+++ b/lib/param_parsing/toml_test.go
@@ -25,6 +25,18 @@ func TestGetParamsTOML_BinaryPath(t *testing.T) {
 	}
 }
 
+func TestGetParamsTOML_InstallPath(t *testing.T) {
+	expected := "/tmp"
+	params := prepare()
+	params, err := getParamsTOML(params)
+	if err != nil {
+		t.Fatalf("Got error %v", err)
+	}
+	if params.InstallPath != expected {
+		t.Errorf("InstallPath not matching. Got %q, expected %q", params.InstallPath, expected)
+	}
+}
+
 func TestGetParamsTOML_Version(t *testing.T) {
 	expected := "1.6.2"
 	params := prepare()
diff --git a/test-data/integration-tests/test_tfswitchtoml/.tfswitch.toml b/test-data/integration-tests/test_tfswitchtoml/.tfswitch.toml
--- a/test-data/integration-tests/test_tfswitchtoml/.tfswitch.toml
+++ b/test-data/integration-tests/test_tfswitchtoml/.tfswitch.toml
@@ -1,5 +1,6 @@
 arch = "amd64"
 bin = "/usr/local/bin/terraform_from_toml"
+install = "/tmp"
 default-version = "1.5.4"
 log-level = "NOTICE"
 product = "opentofu"