A Go tool that provides a visual progress bar for Terraform operations by parsing the JSON output stream. It offers real-time feedback during both planning and apply phases of Terraform operations.
- Real-time progress updates with step-by-step tracking
- Visual progress bar showing completion status
- Support for both planning and apply phases
- Resource operation status tracking (create, modify, delete)
- Error highlighting and display
- Clean, formatted output
- JSON stream parsing for accurate progress tracking
- Seamless integration with Let'em Cook! (LEMC) for browser-based progress display
The progress bar can be integrated with Let'em Cook! (LEMC) to stream the UI to the browser. Here's how to use the ProgressHandler with LEMC:
package main
import (
"fmt"
"io"
"os"
"os/exec"
"strings"
"github.com/jaredfolkins/terraform-loading-bar/progress"
)
func main() {
// Initialize Terraform
initCmd := exec.Command("terraform", "init")
initCmd.Stdout = os.Stdout
initCmd.Stderr = os.Stderr
if err := initCmd.Run(); err != nil {
panic(err)
}
// Create plan
planCmd := exec.Command("terraform", "plan", "-out", "terraform.plan")
planCmd.Stdout = os.Stdout
planCmd.Stderr = os.Stderr
if err := planCmd.Run(); err != nil {
panic(err)
}
// Apply with JSON output
applyCmd := exec.Command("terraform", "apply", "-json", "-auto-approve", "terraform.plan")
applyOutput, err := applyCmd.StdoutPipe()
if err != nil {
panic(err)
}
applyCmd.Stderr = os.Stderr
// Start the apply command
if err := applyCmd.Start(); err != nil {
panic(err)
}
// Create a progress handler
progressHandler := progress.NewProgressHandler(applyOutput)
// Stream the output in real-time with progress bar
for {
line, err := progressHandler.ReadLine()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
if line != "" {
// Use LEMC's trunc verb to update the progress bar in place
fmt.Printf("lemc.html.trunc; %s\n", strings.TrimSpace(line))
}
}
// Wait for the apply command to complete
if err := applyCmd.Wait(); err != nil {
panic(err)
}
}
- The
ProgressHandler
processes Terraform's JSON output stream in real-time - Each line of progress is formatted with a visual progress bar
- Using LEMC's
lemc.html.trunc
verb, each line replaces the previous one in the browser - This creates a smooth, animated progress bar showing:
- Current operation number and total operations
- Visual progress bar
- Resource name and current operation
- Error messages when they occur
The progress bar displays in the following format:
(current_step)[====================](total_steps) resource_name: operation...
For example:
(01)[====================](18) google_compute_network.vpc: Creating...
During the planning phase, it shows:
[ PLANNING ] Planning...
go install github.com/jaredfolkins/terraform-loading-bar@latest
- Initialize your Terraform configuration:
terraform init
- Create a plan file:
terraform plan -out terraform.plan
- Apply the plan with JSON output and pipe it through the loading bar:
terraform apply -json terraform.plan | terraform-loading-bar
You can also use the parser directly in your Go application to stream Terraform output:
package main
import (
"os"
"os/exec"
"github.com/jaredfolkins/terraform-loading-bar/progress"
)
func main() {
// Initialize Terraform
initCmd := exec.Command("terraform", "init")
initCmd.Stdout = os.Stdout
initCmd.Stderr = os.Stderr
if err := initCmd.Run(); err != nil {
panic(err)
}
// Create plan
planCmd := exec.Command("terraform", "plan", "-out", "terraform.plan")
planCmd.Stdout = os.Stdout
planCmd.Stderr = os.Stderr
if err := planCmd.Run(); err != nil {
panic(err)
}
// Apply with JSON output
applyCmd := exec.Command("terraform", "apply", "-json", "-auto-approve", "terraform.plan")
applyOutput, err := applyCmd.StdoutPipe()
if err != nil {
panic(err)
}
applyCmd.Stderr = os.Stderr
// Start the apply command
if err := applyCmd.Start(); err != nil {
panic(err)
}
// Stream the output through the progress bar
if err := progress.ProcessJSONStream(applyOutput, os.Stdout); err != nil {
panic(err)
}
// Wait for the apply command to complete
if err := applyCmd.Wait(); err != nil {
panic(err)
}
}
By default, long messages are truncated to keep the progress bar output on a single line. You can adjust how many characters are retained before the ellipsis is added:
progress.SetTrimLength(60) // allow up to 60 characters
If you want to get the progress output as a string instead of printing it directly, you can use the GetProgressOutput
function:
package main
import (
"os"
"os/exec"
"fmt"
"github.com/jaredfolkins/terraform-loading-bar/progress"
)
func main() {
// Initialize Terraform
initCmd := exec.Command("terraform", "init")
initCmd.Stdout = os.Stdout
initCmd.Stderr = os.Stderr
if err := initCmd.Run(); err != nil {
panic(err)
}
// Create plan
planCmd := exec.Command("terraform", "plan", "-out", "terraform.plan")
planCmd.Stdout = os.Stdout
planCmd.Stderr = os.Stderr
if err := planCmd.Run(); err != nil {
panic(err)
}
// Apply with JSON output
applyCmd := exec.Command("terraform", "apply", "-json", "-auto-approve", "terraform.plan")
applyOutput, err := applyCmd.StdoutPipe()
if err != nil {
panic(err)
}
applyCmd.Stderr = os.Stderr
// Start the apply command
if err := applyCmd.Start(); err != nil {
panic(err)
}
// Get the progress output as a string
output, err := progress.GetProgressOutput(applyOutput)
if err != nil {
panic(err)
}
// Print the output yourself
fmt.Print(output)
// Wait for the apply command to complete
if err := applyCmd.Wait(); err != nil {
panic(err)
}
}
The tool will display any Terraform errors in a clear format:
TERRAFORM ERROR: error message
git clone https://github.com/jaredfolkins/terraform-loading-bar.git
cd terraform-loading-bar
go build
go test ./...
MIT License
Contributions are welcome! Please feel free to submit a Pull Request.