Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
ea5dc46
631: Add STS support for Fabric Client
rrajagopalan-equinix Jun 2, 2025
a6ce8e3
631: Use renewable token source
rrajagopalan-equinix Jun 11, 2025
c386d18
631: First pass at docs for wif support
rrajagopalan-equinix Jun 12, 2025
729877f
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Jun 17, 2025
867906c
631: Linting fixes
rrajagopalan-equinix Jun 17, 2025
df1b63f
631: Feedback
rrajagopalan-equinix Jun 19, 2025
f455290
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Jun 19, 2025
f08b80d
631: Undo sdk changes
rrajagopalan-equinix Jun 26, 2025
e109511
Docs changes
rrajagopalan-equinix Jun 26, 2025
8b138eb
Linting
rrajagopalan-equinix Jun 26, 2025
4fc425d
Linting
rrajagopalan-equinix Jun 26, 2025
8f2fa06
Linting
rrajagopalan-equinix Jun 26, 2025
40ff252
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Jul 3, 2025
811fa16
Use latest sdk + context-aware token source
rrajagopalan-equinix Jul 3, 2025
ce910db
Linting
rrajagopalan-equinix Jul 7, 2025
9054c86
Linting
rrajagopalan-equinix Jul 7, 2025
2cbfe65
Feedback
rrajagopalan-equinix Jul 7, 2025
af0a15d
Feedback
rrajagopalan-equinix Jul 8, 2025
9570aa4
STS setup docs
rrajagopalan-equinix Jul 8, 2025
66e53b3
Feedback
rrajagopalan-equinix Jul 11, 2025
52bc166
Feedback
rrajagopalan-equinix Jul 14, 2025
ff310e1
Feedback
rrajagopalan-equinix Jul 14, 2025
84dd7a3
Gha Testing
rrajagopalan-equinix Jul 16, 2025
3b1c4fd
gha testing
rrajagopalan-equinix Jul 16, 2025
5dfacc8
Gha testing
rrajagopalan-equinix Jul 16, 2025
922e768
gha test
rrajagopalan-equinix Jul 16, 2025
f781395
gha testing
rrajagopalan-equinix Jul 16, 2025
6b2e817
gha testing
rrajagopalan-equinix Jul 16, 2025
bb15253
gha testing
rrajagopalan-equinix Jul 16, 2025
2f63589
gha test
rrajagopalan-equinix Jul 16, 2025
d6fee57
gha test
rrajagopalan-equinix Jul 16, 2025
a3ebaf6
gha testing
rrajagopalan-equinix Jul 16, 2025
3919dea
gha testing
rrajagopalan-equinix Jul 17, 2025
51633c7
gha testing
rrajagopalan-equinix Jul 17, 2025
822c63e
gha testing
rrajagopalan-equinix Jul 17, 2025
fd527b6
gha fixes
rrajagopalan-equinix Jul 17, 2025
fd863c4
Comment out one-time actions
rrajagopalan-equinix Jul 17, 2025
2989256
Feedback
rrajagopalan-equinix Jul 17, 2025
8c91948
Run specific FCR tests
rrajagopalan-equinix Jul 22, 2025
b61eb5e
Feedback
rrajagopalan-equinix Jul 22, 2025
dd23de7
Use single cmd to run test
rrajagopalan-equinix Jul 22, 2025
71e5d16
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Jul 22, 2025
dce43ac
Feedback
rrajagopalan-equinix Jul 22, 2025
52bab5e
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Aug 11, 2025
4d2b3da
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Aug 12, 2025
43808f7
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Aug 14, 2025
47cb8c9
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Aug 21, 2025
1e2080a
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Aug 29, 2025
da35e74
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Sep 4, 2025
11431df
Update API endpoints and curl options in STS WIF guide
rrajagopalan-equinix Sep 10, 2025
592e251
Update API endpoints and curl options in STS WIF guide
rrajagopalan-equinix Sep 10, 2025
db3aa2e
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Sep 10, 2025
cf3fc5d
Refactor PFCR test workflow in CI pipeline
rrajagopalan-equinix Sep 11, 2025
bc0d0a9
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Oct 1, 2025
9668f6c
Add PFCR sweeper step to Fabric acctest workflow
rrajagopalan-equinix Oct 1, 2025
44ce4b8
Add retry logic to OIDC token exchange
rrajagopalan-equinix Oct 2, 2025
ac01068
Add Codecov upload step and fix error formatting
rrajagopalan-equinix Oct 2, 2025
02a99b3
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Oct 20, 2025
d574de3
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Oct 28, 2025
42f6ce9
Update .github/workflows/fabric_acctest.yml
rrajagopalan-equinix Oct 28, 2025
7a82b0b
Refactor STS source token to use env var name
rrajagopalan-equinix Oct 28, 2025
e483981
Update STS source token configuration documentation
rrajagopalan-equinix Oct 28, 2025
02f326f
Expand STS source token env var check in precheck
rrajagopalan-equinix Oct 28, 2025
825887c
Fix indentation in TestAccPreCheck error handling
rrajagopalan-equinix Oct 28, 2025
b82f836
Rename STS config to token exchange config
rrajagopalan-equinix Oct 29, 2025
51bb1dc
Update token exchange scope secret in workflow
rrajagopalan-equinix Oct 29, 2025
b32d706
Merge branch 'main' into fabric-sts-support
rrajagopalan-equinix Oct 30, 2025
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
81 changes: 79 additions & 2 deletions .github/workflows/fabric_acctest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ on:
workflow_dispatch:

permissions:
#actions: write required to update OIDC subject claim template
pull-requests: read
contents: read
id-token: write

jobs:

Expand All @@ -44,6 +46,8 @@ jobs:
steps:
- run: true

# The GitHub OIDC subject claim template has been customized at the repository level to include only the workflow name. Long-term, we should consider using the default template for tests.

build:
name: Build
needs: authorize
Expand Down Expand Up @@ -85,7 +89,6 @@ jobs:
terraform:
- '1.5'
steps:

- name: Check out code into the Go module directory
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
Expand Down Expand Up @@ -161,7 +164,6 @@ jobs:
terraform:
- '1.5'
steps:

- name: Check out code into the Go module directory
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
Expand Down Expand Up @@ -223,6 +225,81 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage_pfcr.txt

test-STS-creds:
name: Matrix Test
needs: build
runs-on: ubuntu-latest
env:
EQUINIX_API_ENDPOINT: "https://uatapi.equinix.com"
EQUINIX_STS_ENDPOINT: "https://sts.uat.equinix.com"
timeout-minutes: 240
strategy:
fail-fast: false
matrix:
version:
- stable
terraform:
- '1.5'
sts_config:
- name: "default"
env_var_name: "EQUINIX_TOKEN_EXCHANGE_SUBJECT_TOKEN"
set_custom_env_var: false
token_exchange_subject_token_env_var: null
- name: "custom"
env_var_name: "CUSTOM_STS_TOKEN"
set_custom_env_var: true
token_exchange_subject_token_env_var: "CUSTOM_STS_TOKEN"
steps:
- id: get_id_token
name: Get GitHub OIDC Token for PFCR
uses: actions/github-script@v6
with:
script: |
try {
const idToken = await core.getIDToken('gha-fcr-client');
console.log('Token generated with audience: gha-fcr-client');
core.setOutput('id_token', idToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be using core.setSecret instead (which would influence how the value is referenced too)

https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is required since Github automatically masks any value that looks like a token, which includes this one

} catch (error) {
console.error('Error getting OIDC token:', error.message);
core.setFailed(`Error getting OIDC token: ${error.message}`);
}
result-encoding: string

- name: Check out code into the Go module directory
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}

- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version-file: './go.mod'
id: go

- name: Get dependencies
run: |
go mod download

- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
with:
terraform_version: ${{ matrix.terraform }}
terraform_wrapper: false

- name: TF Fabric PFCR acceptance tests STS creds
timeout-minutes: 180
env:
TF_ACC: "1"
TF_ACC_FABRIC_CONNECTIONS_TEST_DATA: ${{ secrets.TF_ACC_FABRIC_CONNECTIONS_TEST_DATA }}
TF_ACC_FABRIC_DEDICATED_PORTS: ${{ secrets.TF_ACC_FABRIC_DEDICATED_PORTS }}
TF_ACC_FABRIC_MARKET_PLACE_SUBSCRIPTION_ID: ${{ secrets.TF_ACC_FABRIC_MARKET_PLACE_SUBSCRIPTION_ID }}
TF_ACC_FABRIC_STREAM_TEST_DATA: ${{ secrets.TF_ACC_FABRIC_STREAM_TEST_DATA }}
EQUINIX_TOKEN_EXCHANGE_SCOPE: ${{ secrets.EQUINIX_STS_AUTH_SCOPE_PFCR }}
METAL_AUTH_TOKEN: ${{ secrets.METAL_AUTH_TOKEN }}
${{ matrix.sts_config.env_var_name }}: ${{ steps.get_id_token.outputs.id_token }}
${{ matrix.sts_config.set_custom_env_var && 'EQUINIX_TOKEN_EXCHANGE_SUBJECT_TOKEN_ENV_VAR' || 'SKIP' }}: ${{ matrix.sts_config.token_exchange_subject_token_env_var || '' }}
run: |
go test ./... --run "(TestAccFabricCreatePort2SPConnection_PFCR|TestAccCloudRouterCreateOnlyRequiredParameters_PFCR)" -v -coverprofile coverage_pfcr.txt -covermode=atomic -count 1

upload-test-report:
name: Upload Testing Report
if: always()
Expand Down
20 changes: 20 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The Equinix provider is used to interact with the resources provided by Equinix

For information about obtaining API key and secret required for Equinix Fabric and Network Edge refer to [Generating Client ID and Client Secret key](https://developer.equinix.com/dev-docs/fabric/getting-started/getting-access-token#generating-client-id-and-client-secret) from [Equinix Developer Platform portal](https://developer.equinix.com).

Equinix Fabric also supports authentication using a [Workload Identity Token](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/workload-identity-tokens), which can be used in place of the `client_id` and `client_secret` arguments. Requires an authorization scope and OIDC token from an IdP trusted by Equinix STS. Please note that this is an alpha feature not available for all users. Using workload identity tokens will override client ID/secret, you must use [provider aliases](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations) to manage both workload identity tokens and client ID/secret in a single Terraform configuration.

Interacting with Equinix Metal requires an API auth token that can be generated at [Project-level](https://metal.equinix.com/developers/docs/accounts/projects/#api-keys) or [User-level](https://metal.equinix.com/developers/docs/accounts/users/#api-keys) tokens can be used.

If you are only using Equinix Metal resources, you may omit the Client ID and Client Secret provider configuration parameters needed to access other Equinix resource types (Network Edge, Fabric, etc).
Expand Down Expand Up @@ -44,6 +46,20 @@ provider "equinix" {
}
```

Workload Identity Tokens can be used in service authorization scenarios, like HCP Terraform. Other credential variables are optional for `equinix_fabric_*` resources and datasources when using this method.

```terraform
# Configuration for using Workload Identity Federation
provider "equinix" {
# Desired scope of the requested security token. Must be an Access Policy ERN or a string of the form `roleassignments:<organization_id>`
token_exchange_scope = "roleassignments:<organization_id>"

# The name of the environment variable containing the token exchange subject token
# For example, HCP Terraform automatically sets TFC_WORKLOAD_IDENTITY_TOKEN
token_exchange_subject_token_env_var = "TFC_WORKLOAD_IDENTITY_TOKEN"
}
```

Example provider configuration using `environment variables`:

```sh
Expand Down Expand Up @@ -85,4 +101,8 @@ These parameters can be provided in [Terraform variable files](https://www.terra
- `max_retry_wait_seconds` (Number) Maximum number of seconds to wait before retrying a request.
- `request_timeout` (Number) The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Canceled requests may still result in provisioned resources. (Defaults to `30`)
- `response_max_page_size` (Number) The maximum number of records in a single response for REST queries that produce paginated responses. (Default is client specific)
- `sts_endpoint` (String) The STS API base URL to point to the desired environment. This argument can also be specified with the `EQUINIX_STS_ENDPOINT` shell environment variable. (Defaults to `https://sts.eqix.equinix.com`). Please note that STS is an alpha feature and not available for all users.
- `token` (String) API tokens are generated from API Consumer clients using the [OAuth2 API](https://developer.equinix.com/dev-docs/fabric/getting-started/getting-access-token#request-access-and-refresh-tokens). This argument can also be specified with the `EQUINIX_API_TOKEN` shell environment variable.
- `token_exchange_scope` (String) The scope of the authentication token. Must be an access policy ERN or a string of the form `roleassignments:<org_id>`. This argument can also be specified with the `EQUINIX_TOKEN_EXCHANGE_SCOPE` shell environment variable. Please note that token exchange is an alpha feature and not available for all users.
- `token_exchange_subject_token` (String) The subject token to use for token exchange authentication. Must be an OIDC ID token issued by an OIDC provider trusted by Equinix STS. If not set, the provider will use the environment variable specified in `token_exchange_subject_token_env_var`. Please note that token exchange is an alpha feature and not available for all users.
- `token_exchange_subject_token_env_var` (String) The name of the environment variable containing the subject token for token exchange. This argument can also be specified with the `EQUINIX_TOKEN_EXCHANGE_SUBJECT_TOKEN_ENV_VAR` shell environment variable. (Defaults to `EQUINIX_TOKEN_EXCHANGE_SUBJECT_TOKEN`). Please note that token exchange is an alpha feature and not available for all users.
47 changes: 38 additions & 9 deletions equinix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,31 @@ func Provider() *schema.Provider {
Default: 30,
Description: "Maximum number of seconds to wait before retrying a request.",
},
"token_exchange_scope": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc(config.TokenExchangeScopeEnvVar, ""),
Description: "The scope of the authentication token. Must be an access policy ERN or a string of the form `roleassignments:<org_id>`. This argument can also be specified with the `EQUINIX_TOKEN_EXCHANGE_SCOPE` shell environment variable. Please note that token exchange is an alpha feature and not available for all users.",
},
"sts_endpoint": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc(config.StsEndpointEnvVar, config.DefaultStsBaseURL),
ValidateFunc: validation.IsURLWithHTTPorHTTPS,
Description: fmt.Sprintf("The STS API base URL to point to the desired environment. This argument can also be specified with the `EQUINIX_STS_ENDPOINT` shell environment variable. (Defaults to `%s`). Please note that STS is an alpha feature and not available for all users.", config.DefaultStsBaseURL),
},
"token_exchange_subject_token": {
Type: schema.TypeString,
Optional: true,
Description: "The subject token to use for token exchange authentication. Must be an OIDC ID token issued by an OIDC provider trusted by Equinix STS. If not set, the provider will use the environment variable specified in `token_exchange_subject_token_env_var`. Please note that token exchange is an alpha feature and not available for all users.",
},

"token_exchange_subject_token_env_var": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc(config.TokenExchangeSubjectTokenEnvVarEnvVar, config.DefaultTokenExchangeSubjectTokenEnvVar),
Description: fmt.Sprintf("The name of the environment variable containing the subject token for token exchange. This argument can also be specified with the `EQUINIX_TOKEN_EXCHANGE_SUBJECT_TOKEN_ENV_VAR` shell environment variable. (Defaults to `%s`). Please note that token exchange is an alpha feature and not available for all users.", config.DefaultTokenExchangeSubjectTokenEnvVar),
},
},
DataSourcesMap: datasources,
ResourcesMap: resources,
Expand All @@ -109,15 +134,19 @@ func configureProvider(ctx context.Context, d *schema.ResourceData, p *schema.Pr
rt := d.Get("request_timeout").(int)

config := config.Config{
AuthToken: d.Get("auth_token").(string),
BaseURL: d.Get("endpoint").(string),
ClientID: d.Get("client_id").(string),
ClientSecret: d.Get("client_secret").(string),
Token: d.Get("token").(string),
RequestTimeout: time.Duration(rt) * time.Second,
PageSize: d.Get("response_max_page_size").(int),
MaxRetries: d.Get("max_retries").(int),
MaxRetryWait: time.Duration(mrws) * time.Second,
AuthToken: d.Get("auth_token").(string),
BaseURL: d.Get("endpoint").(string),
ClientID: d.Get("client_id").(string),
ClientSecret: d.Get("client_secret").(string),
Token: d.Get("token").(string),
RequestTimeout: time.Duration(rt) * time.Second,
PageSize: d.Get("response_max_page_size").(int),
MaxRetries: d.Get("max_retries").(int),
MaxRetryWait: time.Duration(mrws) * time.Second,
TokenExchangeScope: d.Get("token_exchange_scope").(string),
StsBaseURL: d.Get("sts_endpoint").(string),
TokenExchangeSubjectToken: d.Get("token_exchange_subject_token").(string),
TokenExchangeSubjectTokenEnvVar: d.Get("token_exchange_subject_token_env_var").(string),
}
meta := providerMeta{}

Expand Down
9 changes: 9 additions & 0 deletions examples/example_4.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Configuration for using Workload Identity Federation
provider "equinix" {
# Desired scope of the requested security token. Must be an Access Policy ERN or a string of the form `roleassignments:<organization_id>`
token_exchange_scope = "roleassignments:<organization_id>"

# The name of the environment variable containing the token exchange subject token
# For example, HCP Terraform automatically sets TFC_WORKLOAD_IDENTITY_TOKEN
token_exchange_subject_token_env_var = "TFC_WORKLOAD_IDENTITY_TOKEN"
}
42 changes: 37 additions & 5 deletions internal/acceptance/acceptance.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Package acceptance provides Utilities and test framework setup for running
// acceptance tests for the Equinix Terraform provider. It handles provider
// configuration, authentication verification, and prerequisite checks for
// testing against Equinix Fabric, Network Edge, and Metal services.
package acceptance

import (
Expand All @@ -20,9 +24,13 @@ const (
)

var (
TestAccProvider *schema.Provider
TestAccProviders map[string]*schema.Provider
TestExternalProviders map[string]resource.ExternalProvider
// TestAccProvider is the Equinix provider instance used for acceptance testing
TestAccProvider *schema.Provider
// TestAccProviders is a map of provider names to their schema.Provider instances used in acceptance tests.
TestAccProviders map[string]*schema.Provider
// TestExternalProviders defines external providers (by name and source) required for acceptance tests.
TestExternalProviders map[string]resource.ExternalProvider
// TestAccFrameworkProvider is the FrameworkProvider instance used for advanced acceptance test scenarios.
TestAccFrameworkProvider *provider.FrameworkProvider
// testAccProviderConfigure ensures Provider is only configured once
//
Expand All @@ -46,6 +54,9 @@ func init() {
TestAccFrameworkProvider = provider.CreateFrameworkProvider(version.ProviderVersion).(*provider.FrameworkProvider)
}

// TestAccPreCheck verifies that the required environment variables are set
// for running acceptance tests. It checks for authentication credentials for
// Equinix Fabric, Network Edge, and Metal services.
func TestAccPreCheck(t *testing.T) {
var err error

Expand All @@ -54,24 +65,45 @@ func TestAccPreCheck(t *testing.T) {
if err == nil {
_, err = env.Get(config.ClientSecretEnvVar)
}

// If neither token nor client ID/secret are configured, check for STS source token
if err != nil {
_, tokenExchangeScopeErr := env.Get(config.TokenExchangeScopeEnvVar)

// Check if either the custom env var name is set, or the default source token is set
_, subjectTokenEnvVarErr := env.Get(config.TokenExchangeSubjectTokenEnvVarEnvVar)
_, defaultSubjectTokenErr := env.Get(config.DefaultTokenExchangeSubjectTokenEnvVar)

if tokenExchangeScopeErr == nil && (subjectTokenEnvVarErr == nil || defaultSubjectTokenErr == nil) {
err = nil
}
}
}

if err == nil {
_, err = env.Get(config.MetalAuthTokenEnvVar)
}

if err != nil {
t.Fatalf("To run acceptance tests, one of '%s' or pair '%s' - '%s' must be set for Equinix Fabric and Network Edge, and '%s' for Equinix Metal",
config.ClientTokenEnvVar, config.ClientIDEnvVar, config.ClientSecretEnvVar, config.MetalAuthTokenEnvVar)
t.Fatalf("To run acceptance tests, one of '%s', pair '%s' - '%s', or pair '%s' - ('%s' or custom env var from '%s') must be set for Equinix Fabric and Network Edge, and '%s' for Equinix Metal",
config.ClientTokenEnvVar, config.ClientIDEnvVar, config.ClientSecretEnvVar,
config.TokenExchangeScopeEnvVar, config.DefaultTokenExchangeSubjectTokenEnvVar,
config.TokenExchangeSubjectTokenEnvVarEnvVar, config.MetalAuthTokenEnvVar)
}

}

// TestAccPreCheckMetal specifically verifies that the Equinix Metal authentication token
// environment variable is set for running Metal-specific acceptance tests.
func TestAccPreCheckMetal(t *testing.T) {
if os.Getenv(config.MetalAuthTokenEnvVar) == "" {
t.Fatalf(missingMetalToken, config.MetalAuthTokenEnvVar)
}
}

// TestAccPreCheckProviderConfigured ensures the provider is properly configured
// before running tests. It uses sync.Once to guarantee the provider is
// configured exactly once across all test executions.
func TestAccPreCheckProviderConfigured(t *testing.T) {
// Since we are outside the scope of the Terraform configuration we must
// call Configure() to properly initialize the provider configuration.
Expand Down
Loading
Loading