Public/Unlock-PSUTerraformStateAWS.ps1

function Unlock-PSUTerraformStateAWS {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-Location).Path,
        
        [Parameter(Mandatory = $false)]
        [string]$LockId,
        
        [Parameter(Mandatory = $false)]
        [string]$AccessKey,
        
        [Parameter(Mandatory = $false)]
        [string]$SecretKey,
        
        [Parameter(Mandatory = $false)]
        [string]$Region = "us-east-2",
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    $originalLocation = Get-Location
    try {
        Write-Host "Validating Terraform directory..."
        if (-not (Test-Path $Path -PathType Container)) {
            throw "Directory does not exist: $Path"
        }

        $terraformFiles = @("*.tf", ".terraform")
        $hasTerraformContent = $false
        foreach ($pattern in $terraformFiles) {
            if (Get-ChildItem -Path $Path -Filter $pattern -ErrorAction SilentlyContinue) {
                $hasTerraformContent = $true
                break
            }
        }
        if (-not $hasTerraformContent) {
            throw "Path does not appear to be a Terraform directory (no .tf files or .terraform directory found): $Path"
        }

        if (-not $LockId) {
            do {
                $LockId = Read-Host "Enter the Terraform Lock ID"
                if ([string]::IsNullOrWhiteSpace($LockId)) {
                    Write-Host "Lock ID cannot be empty. Please try again."
                }
            } while ([string]::IsNullOrWhiteSpace($LockId))
        }
        Write-Host "Lock ID: $LockId"

        Set-Location -Path $Path
        Write-Host "Working in Terraform directory: $Path"

        # Detect backend type
        $tfFiles = Get-ChildItem -Path $Path -Filter "*.tf" -Recurse
        $backendType = ( $tfFiles | Get-Content | Select-String -Pattern 'backend\s+"(\w+)"' -AllMatches |
                        ForEach-Object { $_.Matches.Groups[1].Value } |
                        Select-Object -First 1)

        if (-not $backendType) {
            $backendType = "local"
        }
        Write-Host "Detected backend type: $backendType"

        Write-Host "Initializing Terraform backend..."
        if ($backendType -eq "s3") {
            if ($AccessKey -and $SecretKey) {
                Write-Host "Using provided AWS credentials..."
                $initArgs = @(
                    "init",
                    "-backend-config=access_key=$AccessKey",
                    "-backend-config=secret_key=$SecretKey",
                    "-backend-config=region=$Region"
                )
            } else {
                Write-Host "No AWS credentials provided, using default AWS profile or environment."
                $initArgs = @("init")
            }
        } else {
            Write-Host "Non-S3 backend detected, skipping AWS backend config..."
            $initArgs = @("init", "-get-plugins=false")
        }

        $initResult = & terraform $initArgs 2>&1
        if ($LASTEXITCODE -ne 0) {
            throw "Terraform init failed. Output: $($initResult -join "`n")"
        }
        Write-Host "Terraform backend initialized successfully"

        # Workspaces
        Write-Host "Retrieving available workspaces..."
        $workspaceOutput = terraform workspace list 2>&1
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to list workspaces. Output: $($workspaceOutput -join "`n")"
        }

        $workspaces = @($workspaceOutput | Where-Object { $_ -match '\S' } | 
                     ForEach-Object { 
                         $workspace = $_.Trim()
                         if ($workspace.StartsWith('* ')) {
                             $workspace.Substring(2).Trim()
                         } else {
                             $workspace.Trim()
                         }
                     } | Where-Object { $_ -ne '' })

        if ($workspaces.Count -eq 0) {
            throw "No workspaces found"
        }

        Write-Host "Available Workspaces:"
        for ($i = 0; $i -lt $workspaces.Count; $i++) {
            Write-Host " [$($i+1)] $(@($workspaces)[$i])"
        }

        $selectedWorkspace = $null
        if ($workspaces.Count -eq 1 -or $workspaces -contains "default") {
            $selectedWorkspace = $workspaces[0]
            Write-Host "Only one workspace or default detected, auto-selecting: $selectedWorkspace"
        } else {
            do {
                $choice = Read-Host "Enter the number of the workspace to select (1-$($workspaces.Count)) or press Enter for default"
                if ([string]::IsNullOrWhiteSpace($choice)) {
                    $selectedWorkspace = "default"
                    break
                }
                if ($choice -notmatch '^\d+$') {
                    Write-Host "Please enter a valid number."
                    continue
                }
                $choiceInt = [int]$choice
                if ($choiceInt -lt 1 -or $choiceInt -gt $workspaces.Count) {
                    Write-Host "Please enter a number between 1 and $($workspaces.Count)."
                    continue
                }
                $selectedWorkspace = @($workspaces)[$choiceInt-1]
                break
            } while ($true)
        }

        Write-Host "Selecting workspace: $selectedWorkspace"
        $selectResult = terraform workspace select $selectedWorkspace 2>&1
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to select workspace '$selectedWorkspace'. Output: $($selectResult -join "`n")"
        }
        Write-Host "Workspace '$selectedWorkspace' selected successfully"

        # Only force-unlock if backend = s3
        if ($backendType -eq "s3") {
            if (-not $Force) {
                Write-Host "WARNING: Force unlocking may cause state corruption if another operation is running."
                do {
                    $confirm = Read-Host "Are you sure you want to force unlock the state? (yes/no)"
                    if ($confirm -eq 'yes') { break }
                    elseif ($confirm -eq 'no') { Write-Host "Operation cancelled by user"; return }
                    else { Write-Host "Please type 'yes' or 'no'" }
                } while ($true)
            }

            Write-Host "Unlocking state with Lock ID: $LockId"
            $unlockResult = terraform force-unlock -force $LockId 2>&1
            if ($LASTEXITCODE -ne 0) {
                if ($unlockResult -match "lock.*not found" -or $unlockResult -match "no lock found") {
                    Write-Host "No lock found with ID '$LockId' - the state may already be unlocked"
                } else {
                    throw "Failed to unlock state. Output: $($unlockResult -join "`n")"
                }
            } else {
                Write-Host "State successfully unlocked!"
            }
        } else {
            Write-Host "Local backend detected - no remote lock to unlock."
        }

        Write-Host "Operation completed successfully!"
        Write-Host "Workspace: $selectedWorkspace"
        Write-Host "Directory: $Path"
    }
    catch {
        Write-Host "Error: $($_.Exception.Message)"
        Write-Host "Operation failed"
        exit 1
    }
    finally {
        Set-Location -Path $originalLocation
        Write-Host "Restored to original location: $originalLocation"
    }
}