Terraform API using PowerShell - Workspace Information

4 minute read

"Buy Me A Coffee"

šŸš€Hello there, This is my first post and I hope it helps someone and remember constructive feedback is always welcomešŸš€.

I have been working on getting on getting Workspace information in Terraform Enterprise/Cloud. I manage a large number of workspaces and Terraform Cloud/ Enterprise does not show the terraform version of your managed workspaces and some other info that can be useful to make decisions. Terraform Enterprise gives you an idea of how many workspaces are running on which version but not individually. However, TFC does not ā€¦at least on the free version.

Good thing the Terraform API is quite rich so we can leverage that to gather the information required and make use of the different capabilities that come with it. So I went and start reading on Terraform API for Workspaces documentation and found what I needed to be able to gather the information I needed at this stage.

image-center

I found a few examples using python and even a colleague provide me one. However, I still love PowerShell so why not try getting this info using PowerShell. I did start using the Invoke-RestMethod and found it quite easy to make the calls as per documentation.

What do we need to make the magic?

  1. Terraform API TOKEN
  2. Server name. Could be your Terraform Enterprise server or if using Terraform Cloud app.terraform.io
  3. Terraform Organization in case you have more than 1 šŸ¤”

Based on the documentation this is just enough to start getting some json formatted info

$headers = @{
  Authorization = "Bearer $TF_TOKEN"
}

Invoke-RestMethod  -Uri "https://$Server/api/v2/organizations/$Org/workspaces?page%5Bnumber=$pag%5D" -Method Get -ContentType "application/vnd.api+json" -Headers $headers

My aim was to get the workspace information specifically the workspace name and the version this is running for reporting but also to define processes to ensure workspaces are running on the same version as possible.. I got that information by applying the below. Created an Object and got the information by playing with the json payload. Interestingly enough you need to go through all workspace pages like the gui to get all workpaces info otherwise you will only get the information of the first page only šŸ¤Ø

Gathering Workspace information

try {
  Write-Verbose -Message "connecting and getting workspace info from TFC/TFE using token"
  $page = (Invoke-RestMethod  -Uri "https://$Server/api/v2/organizations/$Org/workspaces?page%5Bnumber=$pag%5D" -Method Get -ContentType "application/vnd.api+json" -Headers $headers -ErrorVariable $ErrorCredential).meta.pagination.'total-pages' 
  While ($pag -le $page) {
    $data = (Invoke-RestMethod  -Uri "https://$Server/api/v2/organizations/$Org/workspaces?page%5Bnumber=$pag%5D" -Method Get -ContentType "application/vnd.api+json" -Headers $headers).data

    foreach ($workspace in $data) {
      $workspaceID = $workspace.id
      Write-Progress -Activity "getting Terraform Workspaces" -Status "Working on worksapce $workspace"  -PercentComplete ((($data.IndexOf($workspace)) / $data.Count) * 100)
      Write-Progress -Activity "Terraform Workspaces" -Status "Done" -PercentComplete 100 -Completed
      $status = (Invoke-RestMethod  -Uri "https://$Server/api/v2/workspaces/$workspaceID/runs" -Method Get -ContentType "application/vnd.api+json" -Headers $headers).data.attributes.status[0]
      $wrk = new-object PSObject
      $wrk | add-member -MemberType NoteProperty -Name "WorkspaceID" -Value $workspace.id
      $wrk | add-member -MemberType NoteProperty -Name "WorkspaceName" -Value $workspace.attributes.name
      $wrk | add-member -MemberType NoteProperty -Name "Version" -Value $workspace.attributes.'terraform-version'
      $wrk | add-member -MemberType NoteProperty -Name "Status" -Value $status

      $workspaces += $wrk
    }
    $pag++
  }
}
catch {
  IF ($ErrorCredential) {
    Write-Warning -Message  "Review - Credentials to connect to TFC/TFE"
  }
  Write-Warning -Message $error[0].exception.message
  break
}

Workspace Output Visualization

After I have what I wanted I got another task I wanted to achieve.. This time I wanted to be able to trigger workspaces which for some reason are in errored state. To achieve that, I used the old Out-GridView which helped me to visualize and sort of deep dive into the data returned giving some flexibility like filtering from one paneā€¦ something you can achieve in the Terraform portal butā€¦. too many clicks and you cannot trigger them in bulk. For my demo, I only have two workspaces but imagine having a few hundredsā€¦ this was handy.

image-center

TIP if you using a Mac you can install this tool on PowerShell 7 āš” by running Install-Module Microsoft.PowerShell.GraphicalTools

$workspaces | Out-GridView -OutputMode Multiple -Title ā€˜Please select the workspace/worspaces to run.ā€™

Triggering Multiple Workspaces

After being able to select the workspace/workspaces the next step is to be able to trigger the run for all of them. I started using the ForEach-Object -Parallel to trigger the API call but given I am not waiting for a script execution the time gain is minimal but worth to play with. To achieve this, I read about the Terraform Runs API here you will find the payload required and the method to be used to make the API calls.

$instances = ($workspaces | Out-GridView -OutputMode Multiple -Title ā€˜Please select the workspace/worspaces to run.ā€™).WorkspaceID 
$instances | ForEach-Object -parallel {
  $body = @"
    {
        "data": {
            "attributes": {
                "is-destroy": false,
                "message": "$using:message"
            },
            "type": "runs",
            "relationships": {
                "workspace": {
                    "data": {
                        "type": "workspaces",
                        "id": "$_"
                    }
                }
            },
            "configuration-version": {}
        }
    }
"@
 
  try {
    Invoke-RestMethod  -Uri "https://$using:server/api/v2/runs" -Method POST -ContentType "application/vnd.api+json" -Headers $using:headers -Body $body -ErrorVariable $ErrorCredential | Out-Null
  }
  catch {
    IF ($ErrorCredential) {
      Write-Warning -Message  "Review - Credentials to connect to TFC/TFE"
    }
    Write-Warning -Message $error[0].exception.message
  }
}

The Code in Action

The below is a quick demo of the script in action.
image-center

Summary

The script definitely can be more robust and improved but this is just the beginning of the amount of actions that you can automate on different scenarios. For example, triggering the run from an Azure DevOps pipeline as part of full CI/CD deployment where TFE/TFC is your backend for your state file. Reporting can be useful too as you generate reports and/or dashboards to get more insights of your Terraform consumption products

Code Download

The code is available on Github on the PowerShell repository.

šŸš“ā€ā™‚ļø If you enjoyed this blog, you can empower me with some caffeine to continue working in new content šŸš“ā€ā™‚ļø.

"Buy Me A Coffee"

Comments