TFSwitchPS.psm1
#Region Dependencies $choco = Invoke-Expression "choco list terraform -le" if ( $choco ){ $chocoPackageInstalls = $choco | Where-Object {$_ -match "\d{1,} packages installed" } [int]$packageCount = $chocoPackageInstalls.Substring(0,2).trim() if ( $packageCount ) { Write-Warning "Chocolatey Terraform Packages detected. Uninstall in order to use tfswitch." $choco exit 1 } } #endregion #region Configuration $env:TFSWITCH_BASEDIR = ( Resolve-Path ~\.terraform ).Path #endregion #region LoadFunctions $PublicFunctions = @( Get-ChildItem -Path "$PSScriptRoot/Public/*.ps1" -ErrorAction SilentlyContinue ) $PrivateFunctions = @( Get-ChildItem -Path "$PSScriptRoot/Private/*.ps1" -ErrorAction SilentlyContinue ) # Dot source the functions foreach ($file in @($PublicFunctions + $PrivateFunctions)) { try { . $file.FullName } catch { $exception = ([System.ArgumentException]"Function not found") $errorId = "Load.Function" $errorCategory = 'ObjectNotFound' $errorTarget = $file $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Failed to import function $($file.BaseName)" throw $errorItem } } # Export-ModuleMember -Function $PublicFunctions.BaseName -Alias * #endregion Function Set-TerraformVersion { [CmdletBinding(DefaultParameterSetName = 'none')] [alias('tfswitch','tfs')] param ( # Install a version of terraform [Parameter(ParameterSetName = 'Install')] [alias('i')] [switch] $Install, [Parameter(ParameterSetName = 'Uninstall')] [alias('d')] [switch] $Uninstall, # List installed TF Versions [Parameter(ParameterSetName = 'List')] [alias('l')] [switch] $List, # Set the active version of Terraform [Parameter(ParameterSetName = 'Set')] [alias('s')] [switch] $Set, # Set the active version of Terraform [Parameter(ParameterSetName = 'Unset')] [alias('u')] [switch] $UnSet, # Specific version of Terraform to view, install, or set as active [Parameter(ParameterSetName = 'Set',Position = 0, Mandatory = $true)] [Parameter(ParameterSetName = 'Install', Position = 0, Mandatory = $true)] [Parameter(ParameterSetName = 'List', Position = 0)] [Parameter(ParameterSetName = 'Uninstall', Position = 0)] [Parameter(ParameterSetName = 'none', Position = 0)] [alias('v')] [string] $Version, #When listing versions, use to get a list of versions available for install [Parameter(ParameterSetName = 'List')] [alias('r')] [switch] $Remote ) Begin { $baseInstallDir = $home + '\.terraform' if ( -not ( Test-Path $baseInstallDir ) ) { Write-Verbose 'Terraform Base Directory Missing' New-Item $baseInstallDir -Force -ItemType Directory | Out-Null } } Process { if ( $PSCmdlet.ParameterSetName -eq 'none' ) { Write-Verbose 'Running Workflow [default]' if ( $Version ) { tfswitch -Set -Version $Version } else { Get-TerraformActiveVersion } } if ( $PSCmdlet.ParameterSetName -eq 'Install' ) { Write-Verbose 'Running Workflow [Install]' $tfTargetVersion = Get-TerraformRemoteVersionList -Version $Version if ( [string]::IsNullOrEmpty($tfTargetVersion) ) { Write-Warning "Terraform version [$Version] Not found" return } if ( $tfTargetVersion.isInstalled ) { return $tfTargetVersion } else { Install-TerraformVersion -Version $Version } } if ( $PSCmdlet.ParameterSetName -eq 'Uninstall' ) { Write-Verbose 'Running "Install" Workflow' $tfTargetVersion = Get-TerraformInstalledVersionList -Version $Version if ( [string]::IsNullOrEmpty($tfTargetVersion) ) { Write-Warning "Terraform version [$Version] Not found" return } if ( $tfTargetVersion.isActive ) { Write-Warning 'Version Currently set as active. Unset and try again.' return } $versionPath = Split-Path $tfTargetVersion.Path try { Remove-Item $versionPath -Force -Confirm:$false -Recurse -ErrorAction Stop Write-Warning "Terraform Version [$Version] deleted successfully" } catch { Write-Warning "Failed to delete Terraform Version [$Version]" } } if ( $PSCmdlet.ParameterSetName -eq 'Set' ) { Write-Verbose 'Running Workflow [Set]' Set-TerraformActiveVersion -Version $Version Get-TerraformActiveVersion } if ( $PSCmdlet.ParameterSetName -eq 'Unset' ) { Write-Verbose 'Running Workflow [Unset]' Install-TerraformActiveVersion Get-TerraformActiveVersion } if ( $PSCmdlet.ParameterSetName -eq 'List' ) { Write-Verbose 'Running workflow [List]' if ( $Remote ) { if ( $Version ) { Get-TerraformRemoteVersionList -Version $Version } else { Get-TerraformRemoteVersionList } } else { if ( $Version ) { Get-TerraformInstalledVersionList -Version $Version }else { Get-TerraformInstalledVersionList } } } } End { } } Function Clear-TerraformActiveVersion { [CmdletBinding()] # [alias('Clear-TFActiveVersion')] param ( ) Write-Verbose "Clearing Active Terraform Version State" Remove-Item Env:\TFSWITCH_PATH -ErrorAction SilentlyContinue Remove-Item Env:\TFSWITCH_VERSION -ErrorAction SilentlyContinue $pathTemp = $env:Path.Clone() $env:Path = ($pathTemp.Split(';') | Where-Object {$_ -notmatch "\.terraform" }) -join ';' [System.Environment]::SetEnvironmentVariable( 'TFSWITCH_VERSION', $null, [System.EnvironmentVariableTarget]::User) [System.Environment]::SetEnvironmentVariable('path', $env:Path ,[System.EnvironmentVariableTarget]::User) } Function Get-TerraformActiveVersion{ [CmdletBinding()] Param() # $tfVersionTest = Invoke-Expression "terraform --version" try { Invoke-Expression "terraform --version" | Where-Object { $_ -match 'Terraform v\d{1,}\.\d{1,}\.\d{1,}' } } catch { Write-Warning 'Terraform not installed or path not set' } } Function Get-TerraformInstalledVersionList { [CmdletBinding()] param ( [Parameter()] [string] $Version ) Begin { Write-Verbose "[$($MyInvocation.MyCommand)] Starting Function" } Process { $tfVersions = Get-ChildItem $env:TFSWITCH_BASEDIR -Recurse -File -Filter 'terraform.exe' -ErrorAction SilentlyContinue if ( !$tfversions ) { Write-Warning "No Terraform Install found in location $env:TFSWITCH_BASEDIR" } elseif ( $Version ) { Write-Verbose "[$($MyInvocation.MyCommand)] Filtering for Version [$Version]" } $return = $tfVersions | ForEach-Object { $versionTemp = Split-Path $_ -Parent | Split-Path -Leaf New-Object psobject -Property ([ordered]@{ Version = $versionTemp isActive = $versionTemp -eq $env:TFSWITCH_VERSION Path = $_.FullName }) } if ( $Version ) { $return = $return | Where-Object { $_.Version -eq $Version } if ( !$return ) { Write-Warning "[$Version] version not found locally." } } return $return } } Function Get-TerraformRemoteVersionList{ [CmdletBinding()] param ( [Parameter()] [string] $Version ) Function parseVersion($htmlInput){ $versionName =$htmlInput.trim().TrimEnd('</a>').Split('>')[1] $version = $htmlInput.TrimStart().Split('"')[1].Split('/').Where({$_})[-1] $versionPath = $htmlInput.Trim().Split('"')[1] $os = if($IsWindows){'windows'}elseif($IsLinux){'linux'}else{throw 'invalid OS'} $arch = if([Environment]::Is64BitOperatingSystem){'amd64'}else{'386'} $href = 'https://releases.hashicorp.com{0}{1}_{2}_{3}.zip' -f $versionPath, $versionName,$os,$arch New-Object psobject -Property ([ordered]@{ Version = $htmlInput.TrimStart().Split('"')[1].Split('/').Where({$_})[-1] Name = $versionName isInstalled = [bool](Get-TerraformInstalledVersionList -Version $version -Verbose:$false -WarningAction SilentlyContinue) Link = $href }) } $splat = @{ Uri = 'https://releases.hashicorp.com/terraform' Verbose = $false ErrorAction = 'Stop' } $versionPattern = "<a href=`"/terraform/\d{1,}\.\d{1,}\.\d{1,}\/" try { $tfReturn = Invoke-WebRequest @splat if ( $tfReturn.StatusCode -eq '200') { $tfReturn.Content -split "`n" | Where-Object { $_ -match $versionPattern -and $_ -like "*$Version*"} | ForEach-Object { parseVersion $_ } | Where-Object {if($Version){$_.Version -eq $Version}else{$true}} } } catch { Write-Warning $($_.Exception.Message) } } Function Install-TerraformVersion { [CmdletBinding()] # [Alias('Install-TFVersion')] param ( [Parameter(Mandatory)] [string] $Version ) Begin { Write-Verbose "[$($MyInvocation.MyCommand)] Starting Function" $splat = @{ Uri = '' OutFile = '' Method = 'Get' ErrorAction = 'Stop' Verbose = $false DisableKeepAlive = $true } } Process { #Make Directory Structure $versionDirectory = Join-Path -Path $env:TFSWITCH_BASEDIR -ChildPath $Version New-Item $versionDirectory -Force -ItemType Directory | Out-Null #Get Remote Version $targetVersion = Get-TerraformRemoteVersionList -Version $Version if ( !$targetVersion ) { Write-Verbose "[$($MyInvocation.MyCommand)] [$Version] version not found on remote." Write-Warning "[$($MyInvocation.MyCommand)] [$Version] version not found" return $null } else { $splat.Uri = $targetVersion.Link $splat.OutFile = "$env:TEMP\$($targetVersion.Name).zip" try { #Download Zip File to temp location Invoke-WebRequest @splat #Unzip to destination Expand-Archive -Path $splat.OutFile -DestinationPath $versionDirectory -Force #Validate File $validatedFile = Test-TFVersion -Version $Version if ( $validatedFile ) { Get-TerraformInstalledVersionList -Version $Version } else { throw 'File Validation Failed' } #Clean up Remove-Item -Path $splat.OutFile -Force -ErrorAction SilentlyContinue } catch { Write-Warning "Failed to Install terraform version [$Version]" Write-Warning ( $_.Exception | Out-String ) return $null } } } } Function Set-TerraformActiveVersion{ [CmdletBinding()] # [Alias('Set-TFActiveVersion')] param ( [Parameter()] [string] $Version ) Begin { Clear-TerraformActiveVersion $targetVersion = Get-TerraformInstalledVersionList -Version $Version } Process { if ( [string]::IsNullOrEmpty($targetVersion) ) { Write-Warning "No active Terraform Version Set" return } else { Write-Verbose "Setting Variable: TFSWITCH_VERSION ;Value: [$Version]" $env:TFSWITCH_VERSION = $Version Write-Verbose "Setting Variable: TFSWITCH_PATH ;Value: [$(Split-Path $targetVersion.Path)]" $env:TFSWITCH_PATH = Split-Path $targetVersion.Path Write-Verbose "Setting Variable: PATH ;Value: [`$env:path + ;$env:TFSWITCH_PATH]" $env:Path = $env:Path + ";$($env:TFSWITCH_PATH)" [System.Environment]::SetEnvironmentVariable('TFSWITCH_VERSION', $env:TFSWITCH_VERSION, [System.EnvironmentVariableTarget]::User) [System.Environment]::SetEnvironmentVariable('TFSWITCH_PATH', $env:TFSWITCH_PATH, [System.EnvironmentVariableTarget]::User) [System.Environment]::SetEnvironmentVariable('path', $env:Path ,[System.EnvironmentVariableTarget]::User) } } } Function Test-TerraformVersion{ [CmdletBinding()] param ( [Parameter()] [string] $Version ) $tfVersion = Get-TerraformInstalledVersionList -Version $Version $versionTest = Invoke-Expression "$($tfVersion.Path) --version" $versionTestVersion = ($versionTest | Where-Object { $_ -match 'Terraform v\d{1,}\.\d{1,}\.\d{1,}' }).Split()[-1].Trim('v') $tfVersion.Version -eq $versionTestVersion } |