Skip to content

Commit

Permalink
Merge pull request #124 from jfrog/add-tfc-workload-identity-token-su…
Browse files Browse the repository at this point in the history
…pport

Add TFC workload identity token support
  • Loading branch information
alexhung authored May 24, 2024
2 parents d6be7bc + ddfcc84 commit d538692
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 147 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
if: |
(startsWith(github.ref, 'refs/tags/') && github.event.base_ref == 'refs/heads/master')
|| (startsWith(github.ref, 'refs/tags/') && github.event.base_ref == 'refs/heads/v6')
(startsWith(github.ref, 'refs/tags/') && github.event.base_ref == 'refs/heads/master')
steps:
-
name: Checkout
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
terraform-provider-*
dist/
vendor/
bin/
.idea/
.github/
.modules/
Expand All @@ -13,5 +14,7 @@ lib/
/resources/
coverage.txt
.scannerwork

tfc-testing/.terraform
tfc-testing/terraform.d
tfc-testing/.terraform.lock.hcl
.DS_Store
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ builds:
flags:
- -trimpath
ldflags:
- '-s -w -X github.com/jfrog/terraform-provider-project/pkg/project.Version={{.Version}}'
- '-s -w -X github.com/jfrog/terraform-provider-project/pkg/project/provider.Version={{.Version}}'
goos:
- freebsd
- windows
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 1.6.0 (May 22, 2024)

FEATURES:

* provider: Add support for Terraform Cloud Workload Identity Token. PR: [#124](https://github.com/jfrog/terraform-provider-project/pull/124)

## 1.5.3 (May 13, 2024)

IMPROVEMENTS:
Expand Down
24 changes: 22 additions & 2 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,21 @@ install: clean build
sed -i.bak '0,/version = ".*"/s//version = "${NEXT_VERSION}"/' sample.tf && rm sample.tf.bak && \
terraform init

install_tfc: clean build_tfc
mkdir -p tfc-testing/${BUILD_PATH} && \
mkdir -p tfc-testing/terraform.d/plugins/registry.terraform.io/jfrog/${PRODUCT}/${NEXT_VERSION}/linux_amd64 && \
mv -v dist/terraform-provider-${PRODUCT}_${GORELEASER_ARCH}/terraform-provider-${PRODUCT}_v${NEXT_VERSION}* tfc-testing/${BUILD_PATH} && \
mv -v dist/terraform-provider-${PRODUCT}_linux_amd64_v1/terraform-provider-${PRODUCT}_v${NEXT_VERSION}* tfc-testing/terraform.d/plugins/registry.terraform.io/jfrog/${PRODUCT}/${NEXT_VERSION}/linux_amd64 && \
sed -i.bak '0,/version = ".*"/s//version = "${NEXT_VERSION}"/' tfc-testing/sample.tf && rm tfc-testing/sample.tf.bak && \
cd tfc-testing && \
terraform providers lock -platform=linux_amd64 -platform=darwin_amd64 -fs-mirror=terraform.d/plugins && \
terraform init

clean:
rm -fR dist terraform.d/ .terraform terraform.tfstate* terraform.d/ .terraform.lock.hcl
rm -fR dist terraform.d/ .terraform terraform.tfstate* .terraform.lock.hcl

clean_tfc:
rm -fR dist tfc-testing/terraform.d/ tfc-testing/.terraform tfc-testing/terraform.tfstate* tfc-testing/.terraform.lock.hcl

release:
@git tag ${NEXT_VERSION} && git push --mirror
Expand All @@ -41,16 +54,23 @@ update_pkg_cache:
build: fmt
GORELEASER_CURRENT_TAG=${NEXT_VERSION} goreleaser build --single-target --clean --snapshot

build_tfc: fmt
GORELEASER_CURRENT_TAG=${NEXT_VERSION} goreleaser build --clean --snapshot --config tfc-testing/.goreleaser.yml

test:
@echo "==> Starting unit tests"
go test $(TEST) -timeout=30s -parallel=4

test_tfc: install_tfc
cd tfc-testing && \
terraform plan

attach:
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient attach $$(pgrep terraform-provider-${PRODUCT})

acceptance: fmt
export TF_ACC=true && \
go test -cover -coverprofile=coverage.txt -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_VERSION}-test'" -v -p 1 -parallel 20 -timeout 20m ./pkg/...
go test -cover -coverprofile=coverage.txt -ldflags="-X '${PKG_VERSION_PATH}/provider.Version=${NEXT_VERSION}-test'" -v -p 1 -parallel 20 -timeout 20m ./pkg/...

# To generate coverage.txt run `make acceptance` first
coverage:
Expand Down
68 changes: 60 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
layout: ""
page_title: "Artifactory Project Provider"
description: |-
The Artifactory Project provider provides resources to interact with project supported by Artifactory.
Expand All @@ -24,11 +23,6 @@ curl -sL ${host}/projects/api/system/licenses/ | jq .
}
```

The following 3 license types (`jq .type`) do **NOT** support APIs:
- Community Edition for C/C++
- JCR Edition
- OSS

## Example Usage

```terraform
Expand Down Expand Up @@ -184,7 +178,9 @@ resource "project_environment" "myenv" {

## Authentication

The Artifactory Project provider supports one type of authentication using Bearer token.
The Artifactory provider supports two ways of authentication. The following methods are supported:
* Bearer Token
* Terraform Cloud OIDC provider

### Bearer Token

Expand All @@ -199,11 +195,67 @@ provider "project" {
}
```

### Terraform Cloud OIDC Provider

If you are using this provider on Terraform Cloud and wish to use dynamic credentials instead of static access token for authentication with JFrog platform, you can leverage Terraform as the OIDC provider.

To setup dynamic credentials, follow these steps:
1. Configure Terraform Cloud as a generic OIDC provider
2. Set environment variable in your Terraform Workspace
3. Setup Terraform Cloud in your configuration

During the provider start up, if it finds env var `TFC_WORKLOAD_IDENTITY_TOKEN` it will use this token with your JFrog instance to exchange for a short-live access token. If that is successful, the provider will the access token for all subsequent API requests with the JFrog instance.

#### Configure Terraform Cloud as generic OIDC provider

Follow [confgure an OIDC integration](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-an-oidc-integration). Enter a name for the provider, e.g. `terraform-cloud`. Use `https://app.terraform.io` for "Provider URL". Choose your own value for "Audience", e.g. `jfrog-terraform-cloud`.

Then [configure an identity mapping](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-identity-mappings) with an empty "Claims JSON" (`{}`), and select the "Token scope", "User", and "Service" as desired.

#### Set environment variable in your Terraform Workspace

In your workspace, add an environment variable `TFC_WORKLOAD_IDENTITY_AUDIENCE` with audience value (e.g. `jfrog-terraform-cloud`) from JFrog OIDC integration above. See [Manually Generating Workload Identity Tokens](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation) for more details.

When a run starts on Terraform Cloud, it will create a workload identity token with the specified audience and assigns it to the environment variable `TFC_WORKLOAD_IDENTITY_TOKEN` for the provider to consume.

#### Setup Terraform Cloud in your configuration

Add `cloud` block to `terraform` block, and add `oidc_provider_name` attribute (from JFrog OIDC integration) to provider block:

```terraform
terraform {
cloud {
organization = "my-org"
workspaces {
name = "my-workspace"
}
}
required_providers {
project = {
source = "jfrog/project"
version = "1.6.0"
}
}
}
provider "project" {
url = "https://myinstance.jfrog.io"
oidc_provider_name = "terraform-cloud"
}
```

**Note:** Ensure `access_token` attribute and `JFROG_ACCESS_TOKEN` env var are not set

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `url` (String) URL of Artifactory. This can also be sourced from the `PROJECT_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set.

### Optional

- `access_token` (String, Sensitive) This is a Bearer token that can be given to you by your admin under `Identity and Access`. This can also be sourced from the `PROJECT_ACCESS_TOKEN` or `JFROG_ACCESS_TOKEN` environment variable. Defauult to empty string if not set.
- `check_license` (Boolean) Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`.
- `url` (String) URL of Artifactory. This can also be sourced from the `PROJECT_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set.
- `oidc_provider_name` (String) OIDC provider name. See [Configure an OIDC Integration](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-an-oidc-integration) for more details.
10 changes: 0 additions & 10 deletions http/http-client.env.json

This file was deleted.

94 changes: 0 additions & 94 deletions http/projects.http

This file was deleted.

31 changes: 27 additions & 4 deletions pkg/project/provider/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ type ProjectProvider struct{}

// ProjectProviderModel describes the provider data model.
type ProjectProviderModel struct {
Url types.String `tfsdk:"url"`
AccessToken types.String `tfsdk:"access_token"`
CheckLicense types.Bool `tfsdk:"check_license"`
Url types.String `tfsdk:"url"`
AccessToken types.String `tfsdk:"access_token"`
OIDCProviderName types.String `tfsdk:"oidc_provider_name"`
CheckLicense types.Bool `tfsdk:"check_license"`
}

// Metadata satisfies the provider.Provider interface for ProjectProvider
Expand Down Expand Up @@ -54,6 +55,13 @@ func (p *ProjectProvider) Schema(ctx context.Context, req provider.SchemaRequest
},
Description: "This is a Bearer token that can be given to you by your admin under `Identity and Access`. This can also be sourced from the `PROJECT_ACCESS_TOKEN` or `JFROG_ACCESS_TOKEN` environment variable. Defauult to empty string if not set.",
},
"oidc_provider_name": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
Description: "OIDC provider name. See [Configure an OIDC Integration](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-an-oidc-integration) for more details.",
},
"check_license": schema.BoolAttribute{
Optional: true,
Description: "Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`.",
Expand Down Expand Up @@ -98,8 +106,23 @@ func (p *ProjectProvider) Configure(ctx context.Context, req provider.ConfigureR
return
}

// Check configuration data, which should take precedence over
oidcAccessToken, err := util.OIDCTokenExchange(ctx, restyClient, config.OIDCProviderName.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Failed OIDC ID token exchange",
err.Error(),
)
return
}

// use token from OIDC provider, which should take precedence over
// environment variable data, if found.
if oidcAccessToken != "" {
accessToken = oidcAccessToken
}

// Check configuration data, which should take precedence over
// environment variable data or OIDC access token, if found.
if config.AccessToken.ValueString() != "" {
accessToken = config.AccessToken.ValueString()
}
Expand Down
Loading

0 comments on commit d538692

Please sign in to comment.