LibreDevOpsHelpers.TfLint/LibreDevOpsHelpers.TfLint.psm1
|
Set-StrictMode -Version Latest function Install-LdoTfLint { <# .SYNOPSIS Installs the TFLint CLI. .DESCRIPTION Installs TFLint with Chocolatey on Windows. On Linux and macOS it downloads the official release binary from GitHub: the version defaults to 'latest' and is resolved at runtime (no hard-pinned version to maintain), and a specific version can be requested. The earlier install script is avoided because it is being retired and discourages unpinned scripts. .PARAMETER Version TFLint version to install: 'latest' (default) or a specific tag like '0.59.1' / 'v0.59.1'. .EXAMPLE Install-LdoTfLint .EXAMPLE Install-LdoTfLint -Version 0.59.1 .OUTPUTS None #> [CmdletBinding()] [OutputType([void])] param( [string]$Version = 'latest' ) $os = (Get-LdoOperatingSystem).ToLower() if ($os -eq 'windows') { Assert-LdoChocoPath Write-LdoLog -Level INFO -Message 'Installing TFLint via Chocolatey on Windows.' choco install tflint -y Assert-LdoCommand -Name @('tflint') Write-LdoLog -Level SUCCESS -Message 'TFLint installed.' return } # Resolve 'latest' to a concrete tag by following the releases/latest redirect (no API token, # no rate-limit concern). curl is run through bash because in PowerShell "curl" is an alias. if ($Version -eq 'latest') { $effectiveUrl = (bash -c "curl -fsSL -o /dev/null -w '%{url_effective}' https://github.com/terraform-linters/tflint/releases/latest").Trim() Assert-LdoLastExitCode -Operation 'resolve latest tflint release' $tag = ($effectiveUrl.TrimEnd('/') -split '/')[-1] } else { $tag = if ($Version.StartsWith('v')) { $Version } else { "v$Version" } } $platform = if ($os -eq 'macos') { 'darwin' } else { 'linux' } $arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64) { 'arm64' } else { 'amd64' } $url = "https://github.com/terraform-linters/tflint/releases/download/$tag/tflint_${platform}_${arch}.zip" $work = Join-Path ([System.IO.Path]::GetTempPath()) ("ldo-tflint-" + [guid]::NewGuid()) New-Item -ItemType Directory -Path $work | Out-Null try { Write-LdoLog -Level INFO -Message "Downloading TFLint $tag from $url" $zip = Join-Path $work 'tflint.zip' Invoke-WebRequest -Uri $url -OutFile $zip Expand-Archive -Path $zip -DestinationPath $work -Force $binary = Join-Path $work 'tflint' & chmod '+x' $binary $dest = '/usr/local/bin/tflint' try { Move-Item -Path $binary -Destination $dest -Force -ErrorAction Stop } catch { bash -c "sudo mv '$binary' '$dest'" Assert-LdoLastExitCode -Operation 'install tflint to /usr/local/bin' } } finally { Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue } Assert-LdoCommand -Name @('tflint') Write-LdoLog -Level SUCCESS -Message "TFLint $tag installed." } function Invoke-LdoTfLint { <# .SYNOPSIS Runs a TFLint lint over a Terraform code path. .DESCRIPTION Runs 'tflint' against a code path. By default it first runs 'tflint --init' to install the plugins declared in the .tflint.hcl configuration, then runs a recursive lint. Throws on findings unless -SoftFail is set. .PARAMETER CodePath Folder to lint. TFLint is invoked with this folder as its working directory. .PARAMETER ConfigFile Path to a .tflint.hcl configuration file. When omitted, TFLint uses the .tflint.hcl in the code path, if present. .PARAMETER Recursive Lint the code path and all of its subdirectories. Defaults to true. .PARAMETER Init Run 'tflint --init' before linting to install configured plugins. Defaults to true. .PARAMETER SoftFail When set, findings are logged as a warning instead of throwing. .PARAMETER ExtraArgs Additional arguments passed through to tflint. .EXAMPLE Invoke-LdoTfLint -CodePath ./terraform .EXAMPLE Invoke-LdoTfLint -CodePath ./terraform -ConfigFile ./.tflint.hcl -SoftFail .OUTPUTS None #> [CmdletBinding()] [OutputType([void])] param( [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$CodePath, [string]$ConfigFile, [bool]$Recursive = $true, [bool]$Init = $true, [switch]$SoftFail, [string[]]$ExtraArgs = @() ) if (-not (Test-Path $CodePath)) { throw "Code path not found: $CodePath" } if ($ConfigFile -and -not (Test-Path $ConfigFile)) { throw "TFLint config file not found: $ConfigFile" } $commonArgs = @("--chdir=$CodePath") if ($ConfigFile) { $commonArgs += "--config=$((Resolve-Path $ConfigFile).Path)" } if ($Init) { $initArgs = $commonArgs + '--init' Write-LdoLog -Level INFO -Message "Initialising TFLint plugins: tflint $($initArgs -join ' ')" & tflint @initArgs if ($LASTEXITCODE -ne 0) { throw "TFLint plugin init failed (exit $LASTEXITCODE)." } } $lintArgs = $commonArgs if ($Recursive) { $lintArgs += '--recursive' } $lintArgs += $ExtraArgs Write-LdoLog -Level INFO -Message "Executing TFLint: tflint $($lintArgs -join ' ')" # Capture the lint output so it can be re-shown in the end-of-run findings summary, and print it. $report = & tflint @lintArgs 2>&1 $code = $LASTEXITCODE $reportText = (($report | Out-String) -replace '\x1b\[[0-9;]*m', '').TrimEnd() Write-Host $reportText if ($code -eq 0) { Write-LdoLog -Level SUCCESS -Message 'TFLint completed with no findings.' Add-LdoFinding -Tool 'tflint' -Target $CodePath -Status 'PASS' -Summary 'no findings' -Detail $reportText } elseif ($SoftFail) { Write-LdoLog -Level WARN -Message "TFLint found issues (exit $code); continuing because -SoftFail." Add-LdoFinding -Tool 'tflint' -Target $CodePath -Status 'WARN' -Summary "findings (soft-fail, exit $code)" -Detail $reportText } else { Add-LdoFinding -Tool 'tflint' -Target $CodePath -Status 'FAIL' -Summary "findings (exit $code)" -Detail $reportText throw "TFLint failed (exit $code)." } } Export-ModuleMember -Function ` Invoke-LdoTfLint, ` Install-LdoTfLint |