diff --git a/.goreleaser.yml b/.goreleaser.yml index ede4e136..7c29d33d 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -8,9 +8,6 @@ before: - go mod tidy builds: - env: - # goreleaser does not work with CGO, it could also complicate - # usage by users in CI/CD systems like Terraform Cloud where - # they are unable to install libraries. - CGO_ENABLED=0 mod_timestamp: '{{ .CommitTimestamp }}' flags: diff --git a/CHANGELOG.md b/CHANGELOG.md index da30920d..0b09c74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 12.3.0 (October 16, 2024). Tested on Artifactory 7.90.14 with Terraform 1.9.7 and OpenTofu 1.8.3 + +IMPROVEMENTS: + +* provider: Add `tfc_credential_tag_name` configuration attribute to support use of different/[multiple Workload Identity Token in Terraform Cloud Platform](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation#generating-multiple-tokens). Issue: [#68](https://github.com/jfrog/terraform-provider-shared/issues/68) PR: [#1097](https://github.com/jfrog/terraform-provider-artifactory/pull/1097) + ## 12.2.0 (October 14, 2024). Tested on Artifactory 7.90.14 with Terraform 1.9.7 and OpenTofu 1.8.3 IMPROVEMENTS: diff --git a/GNUmakefile b/GNUmakefile index 13f75b1d..d8496f4c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -3,10 +3,13 @@ PRODUCT=artifactory GO_ARCH=$(shell go env GOARCH) TARGET_ARCH=$(shell go env GOOS)_${GO_ARCH} GORELEASER_ARCH=${TARGET_ARCH} +LINUX_GORELEASER_ARCH=linux_${GO_ARCH} ifeq ($(GO_ARCH), amd64) GORELEASER_ARCH=${TARGET_ARCH}_$(shell go env GOAMD64) +LINUX_GORELEASER_ARCH:=${LINUX_GORELEASER_ARCH}_$(shell go env GOAMD64) endif + PKG_NAME=pkg/artifactory # if this path ever changes, you need to also update the 'ldflags' value in .goreleaser.yml PROVIDER_VERSION?=$(shell git describe --tags --abbrev=0 | sed -n 's/v\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1.\2.\3/p') @@ -26,6 +29,7 @@ TF_ACC_PROVIDER_HOST="registry.opentofu.org" endif BUILD_PATH=terraform.d/plugins/${REGISTRY_HOST}/jfrog/${PRODUCT}/${NEXT_PROVIDER_VERSION}/${TARGET_ARCH} +LINUX_BUILD_PATH=terraform.d/plugins/${REGISTRY_HOST}/jfrog/${PRODUCT}/${NEXT_PROVIDER_VERSION}/linux_amd64 SONAR_SCANNER_VERSION?=4.7.0.2747 SONAR_SCANNER_HOME?=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-macosx @@ -36,10 +40,14 @@ default: build install: clean build mkdir -p ${BUILD_PATH} && \ + mkdir -p ${LINUX_BUILD_PATH} && \ mv -v dist/terraform-provider-${PRODUCT}_${GORELEASER_ARCH}/terraform-provider-${PRODUCT}_v${NEXT_PROVIDER_VERSION}* ${BUILD_PATH} && \ sed -i.bak 's/version = ".*"/version = "${NEXT_PROVIDER_VERSION}"/' sample.tf && rm sample.tf.bak && \ ${TERRAFORM_CLI} init + # move this line up when testing on TFC + # mv -v dist/terraform-provider-${PRODUCT}_${LINUX_GORELEASER_ARCH}/terraform-provider-${PRODUCT}_v${NEXT_PROVIDER_VERSION}* ${LINUX_BUILD_PATH} && \ + clean: rm -fR dist terraform.d/ .terraform terraform.tfstate* terraform.d/ .terraform.lock.hcl @@ -53,7 +61,7 @@ update_pkg_cache: GOPROXY=https://proxy.golang.org GO111MODULE=on go get github.com/jfrog/terraform-provider-${PRODUCT}@v${PROVIDER_VERSION} build: fmt - GORELEASER_CURRENT_TAG=${NEXT_PROVIDER_VERSION} goreleaser build --single-target --clean --snapshot + GORELEASER_CURRENT_TAG=${NEXT_PROVIDER_VERSION} goreleaser build --clean --snapshot --single-target test: @echo "==> Starting unit tests" diff --git a/docs/index.md b/docs/index.md index 1427e606..ebd982ba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -107,6 +107,8 @@ In your workspace, add an environment variable `TFC_WORKLOAD_IDENTITY_AUDIENCE` 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. +See [Generating Multiple Tokens](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation#generating-multiple-tokens) on HCP Terraform for more details on using different tokens. + #### 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: @@ -131,6 +133,7 @@ terraform { provider "artifactory" { url = "https://myinstance.jfrog.io" oidc_provider_name = "terraform-cloud" + tfc_credential_tag_name = "JFROG" } ``` @@ -144,4 +147,4 @@ The following arguments are supported: * `access_token` - (Optional) This can also be sourced from `JFROG_ACCESS_TOKEN` or `ARTIFACTORY_ACCESS_TOKEN` environment variables. * `api_key` - (Optional, deprecated) API key for api auth. * `oidc_provider_name` - (Optional) 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` - (Optional) Toggle for pre-flight checking of Artifactory license. Default to `true`. +* `tfc_credential_tag_name` - (Optional) Terraform Cloud Workload Identity Token tag name. Use for generating multiple TFC workload identity tokens. When set, the provider will attempt to use env var with this tag name as suffix. **Note:** this is case sensitive, so if set to `JFROG`, then env var `TFC_WORKLOAD_IDENTITY_TOKEN_JFROG` is used instead of `TFC_WORKLOAD_IDENTITY_TOKEN`. See [Generating Multiple Tokens](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation#generating-multiple-tokens) on HCP Terraform for more details. diff --git a/go.mod b/go.mod index d3539d1a..59b475b5 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 - github.com/jfrog/terraform-provider-shared v1.25.5 + github.com/jfrog/terraform-provider-shared v1.26.0 github.com/samber/lo v1.47.0 github.com/sethvargo/go-password v0.3.1 github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index 96c95119..19c5be87 100644 --- a/go.sum +++ b/go.sum @@ -134,8 +134,8 @@ github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jfrog/terraform-provider-shared v1.25.5 h1:+hal/9yDAIt2mZljDR8Ymie28yAHr8CAkfthwQ3O3bM= -github.com/jfrog/terraform-provider-shared v1.25.5/go.mod h1:QthwPRUALElMt2RTGqoeB/3Vztx626YPBzIAoqEp0w0= +github.com/jfrog/terraform-provider-shared v1.26.0 h1:xfJfKcgejlFkIyo6VLJPzNtEVfbTYIiGKD2PWysdgw4= +github.com/jfrog/terraform-provider-shared v1.26.0/go.mod h1:IPwXN48K3uzJNDmT2x6zFGa5IS0KG2AK7jnQR2H4G1A= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= diff --git a/pkg/artifactory/provider/framework.go b/pkg/artifactory/provider/framework.go index f0cb52a5..5b310754 100644 --- a/pkg/artifactory/provider/framework.go +++ b/pkg/artifactory/provider/framework.go @@ -35,10 +35,11 @@ type ArtifactoryProvider struct{} // ArtifactoryProviderModel describes the provider data model. type ArtifactoryProviderModel struct { - Url types.String `tfsdk:"url"` - AccessToken types.String `tfsdk:"access_token"` - ApiKey types.String `tfsdk:"api_key"` - OIDCProviderName types.String `tfsdk:"oidc_provider_name"` + Url types.String `tfsdk:"url"` + AccessToken types.String `tfsdk:"access_token"` + ApiKey types.String `tfsdk:"api_key"` + OIDCProviderName types.String `tfsdk:"oidc_provider_name"` + TFCCredentialTagName types.String `tfsdk:"tfc_credential_tag_name"` } // Metadata satisfies the provider.Provider interface for ArtifactoryProvider @@ -79,6 +80,13 @@ func (p *ArtifactoryProvider) Schema(ctx context.Context, req provider.SchemaReq }, 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.", }, + "tfc_credential_tag_name": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + Description: "Terraform Cloud Workload Identity Token tag name. Use for generating multiple TFC workload identity tokens. When set, the provider will attempt to use env var with this tag name as suffix. **Note:** this is case sensitive, so if set to `JFROG`, then env var `TFC_WORKLOAD_IDENTITY_TOKEN_JFROG` is used instead of `TFC_WORKLOAD_IDENTITY_TOKEN`. See [Generating Multiple Tokens](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation#generating-multiple-tokens) on HCP Terraform for more details.", + }, }, } } @@ -119,19 +127,22 @@ func (p *ArtifactoryProvider) Configure(ctx context.Context, req provider.Config return } - oidcAccessToken, err := util.OIDCTokenExchange(ctx, restyClient, config.OIDCProviderName.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - "Failed OIDC ID token exchange", - err.Error(), - ) - return - } + oidcProviderName := config.OIDCProviderName.ValueString() + if oidcProviderName != "" { + oidcAccessToken, err := util.OIDCTokenExchange(ctx, restyClient, oidcProviderName, config.TFCCredentialTagName.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 + // 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 diff --git a/pkg/artifactory/provider/sdkv2.go b/pkg/artifactory/provider/sdkv2.go index aef2924e..82fa894a 100644 --- a/pkg/artifactory/provider/sdkv2.go +++ b/pkg/artifactory/provider/sdkv2.go @@ -48,6 +48,12 @@ func SdkV2() *schema.Provider { ValidateDiagFunc: validator.StringIsNotEmpty, 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.", }, + "tfc_credential_tag_name": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validator.StringIsNotEmpty, + Description: "Terraform Cloud Workload Identity Token tag name. Use for generating multiple TFC workload identity tokens. When set, the provider will attempt to use env var with this tag name as suffix. **Note:** this is case sensitive, so if set to `JFROG`, then env var `TFC_WORKLOAD_IDENTITY_TOKEN_JFROG` is used instead of `TFC_WORKLOAD_IDENTITY_TOKEN`. See [Generating Multiple Tokens](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/manual-generation#generating-multiple-tokens) on HCP Terraform for more details.", + }, }, ResourcesMap: resourcesMap(), @@ -85,7 +91,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVer } if v, ok := d.GetOk("oidc_provider_name"); ok { - oidcAccessToken, err := util.OIDCTokenExchange(ctx, restyClient, v.(string)) + oidcAccessToken, err := util.OIDCTokenExchange(ctx, restyClient, v.(string), d.Get("tfc_credential_tag_name").(string)) if err != nil { return nil, diag.FromErr(err) }