Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on:
pull_request:

permissions:
contents: read

jobs:
lint:
name: Lint and Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod

- name: Run golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: latest

- name: Run go test
run: go test ./... -v
33 changes: 33 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
goreleaser:
name: GoReleaser
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
distribution: goreleaser
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin/tfcloud
48 changes: 48 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright IBM Corp. 2021, 2026

version: "2"
run:
tests: true
linters:
enable:
- bodyclose
- gocritic
- godot
- misspell
- revive
- staticcheck
- unconvert
settings:
errcheck:
check-blank: true
misspell:
locale: US
exclusions:
generated: lax
rules:
- linters:
- bodyclose
- errcheck
- revive
path: _test\.go
- path: (.+)\.go$
text: ifElseChain
- path: (.+)\.go$
text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- goimports
settings:
goimports:
local-prefixes:
- github.com/hashicorp/hcloud
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
26 changes: 26 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright IBM Corp. 2021, 2026

version: 2
before:
hooks:
- make go/tidy
builds:
- id: default
main: .
env:
- CGO_ENABLED=0
mod_timestamp: "{{ .CommitTimestamp }}"
flags:
- -trimpath
- -buildvcs=false
ldflags:
- "-s -w -X github.com/hashicorp/tfcloud/internal/config.version={{.Version}} -X github.com/hashicorp/tfcloud/internal/config.Commit={{.Commit}} -X github.com/hashicorp/tfcloud/internal/config.committedTime={{.CommitDate}}"
goos:
- linux
- darwin
goarch:
- amd64
- arm64
archives:
- formats: tar.gz
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ARG base_image=docker.artifactory.hashicorp.engineering/ubuntu:24.04
FROM ${base_image}

ARG PRODUCT_NAME
ARG PRODUCT_VERSION
# TARGETARCH and TARGETOS are set automatically when --platform is provided.
ARG TARGETOS TARGETARCH
ARG BUILD_DIRECTORY=dist/$TARGETOS/$TARGETARCH
ENV BIN_DIR=$BUILD_DIRECTORY

LABEL maintainer="HCP Terraform Support <tf-cloud@hashicorp.support>"
LABEL "com.hashicorp.${PRODUCT_NAME}.version"="${PRODUCT_VERSION}"
LABEL name=$PRODUCT_NAME
LABEL vendor="HashiCorp"
LABEL version=$PRODUCT_VERSION

RUN apt-get -y clean
RUN apt-get -y update && apt-get -y dist-upgrade

RUN apt-get -y install ca-certificates jq unzip curl

RUN groupadd --system tfcloud && useradd --system --create-home --gid tfcloud tfcloud

USER tfcloud
RUN mkdir /home/tfcloud/bin
COPY --chown=tfcloud $BIN_DIR/tfcloud /home/tfcloud/bin/

RUN mkdir -p /home/tfcloud/.config/tfcloud

ENV PATH=$PATH:/local/bin

WORKDIR /home/tfcloud

ENTRYPOINT ["/home/tfcloud/bin/tfcloud"]
7 changes: 7 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright 2026 IBM

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
SHELL=/usr/bin/env bash
NAME=tfcloud
BIN_PATH ?= bin/$(NAME)

ifeq ($(GOARCH), arm64)
GOARCH = arm64
else ifeq ($(GOARCH), s390x)
GOARCH = s390x
else
GOARCH = amd64
endif

default: $(BIN_PATH)

.PHONY: linux
linux:
GOOS=linux GOARCH=$(GOARCH) $(MAKE) bin

.PHONY: docker
docker: linux
docker build --platform=linux/$(GOARCH) --build-arg BUILD_DIRECTORY="bin" -t hashicorp/$(NAME):latest .

.PHONY: bin
bin: $(BIN_PATH)

.PHONY: $(BIN_PATH)
$(BIN_PATH):
CGO_ENABLED=0 go build -o $(BIN_PATH) -trimpath -buildvcs=false ./cmd

.PHONY: clean
clean:
rm -rf $(CURDIR)/$(dir $(BIN_PATH))

128 changes: 127 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,127 @@
# tfcloud
## tfcloud: The HCP Terraform CLI

Effectively interact with the HCP Terraform platform.

#### Quick Start

tfcloud uses a host-centric, layered configuration with a logical precedence. Configuration commands
do not yet exist in the CLI, so start by writing this file to `$HOME/.config/tfcloud/tfcloud.hcl`
(or `%AppData%/tfcloud/tfcloud.hcl` on Windows) substituting your own hostname, token, and organization.

```hcl
profile "default" "app.terraform.io" {
token = "your-token"
organization = "user-org"
}
```

```
# Migrate a tfvars file to the current workspace
tfcloud variable import bigsecret.tfvars

# Migrate a tfvars file to a new variable set
tfcloud variable import bigsecret.tfvars -variable-set-name "production"

# Migrate ENV variables available to the current workspace
tfcloud variable import -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY

# Execute any API v2 GET query
tfcloud api /account/details # Table format
tfcloud api /organizations -json # JSON format

# Execute any POST query by specifying -a for request body attributes in key=value format or -i for raw request body input
tfcloud api /organizations/acme/projects -a "name=my-project" -a "description=it\'s a very fine project"

# ...or use a JSON input file as the body
tfcloud api /organizations/acme/projects -input my-project.json

# ...or use stdin as the request body
./generate_hcptf_run.sh | tfcloud api /runs -input -

# If using parameters in a GET request, set the method to GET.
# This example fetches all pages of data (up to 1000 items) and sorts by created-at descending
tfcloud api /organizations/acme/workspaces -paginate -method GET -f "sort=-created-at"
```

#### Configuration Reference

**Profile-level Configuration**

Linux/MacOS: `~/.config/tfcloud/tfcloud.hcl`
Windows: `%AppData%/tfcloud/tfcloud.hcl`

**Working Directory Configuration**

Working directory config overwrites profile-level config, when available.

`.tfcloud.hcl`

**Token created by `terraform login`**

`~/.terraform.d/credentials.tfrc.json` is checked for the configured hostname if the token is not set by configuration file.

**Token in Environment Variables**

`TFCLOUD_TOKEN`: An API token to use in conjunction with the default profile, only used if token is not set by any other configuration file.

`TFCLOUD_TOKEN_<profile>`: Reserved for future use with multiple profiles.

`TF_TOKEN_<hostname>`: An API token to use with the specified hostname with punycode formatting, e.g. `TF_TOKEN_app_terraform_io`, only used if the token is not specified in any other way.


#### Usage

You can use `tfcloud -help` for detailed usage instructions.

**`tfcloud api <path> [flags]`**

Perform an API request.

`-H, -header <key:value>`
Add a HTTP request header in key:value format

`-i, -input <file>`
The file to use as body for the HTTP request (use "-" to read from standard input)

`-X, -method <string>`
The HTTP method for the request (default "GET", unless using -a attributes)

`-t, -type <string>`
When used with a JSON:API request body for POST/PATCH, the resource type (default to the resource implied by the path)

`-paginate`
Make additional HTTP requests to fetch all pages of results but emit in a streamable manner

`-a, -attribute <key=value>`
Add a typed resource attribute to the request body in key=value format

`-f, -field <key=value>`
Add a query string parameter to the request URL in key=value format

`-agent`
Print the raw response body.

`-json`
Print the raw response body, colorized, if a terminal is attached.

`-v, -verbose`
Log HTTP request and response details to stderr

**`tfcloud variable import [tfvars-file] [flags]`**

Import variables from a tfvars file or the process environment into the current workspace or a variable set.

`-e <name>`
Import an environment variable by name. Repeat to import multiple values.

`-variable-set-name <name>`
Target a variable set by name instead of the current workspace.

`-organization <string>`
Organization name. Optional when it can be resolved from the default organization in `tfcloud.hcl` or local Terraform configuration.

`-workspace <string>`
Override the target workspace name.

`-overwrite`
Update matching existing variables instead of failing when duplicates are found.
Loading
Loading