diff --git a/docs/guides/2-provider-configuration.md b/docs/guides/2-provider-configuration.md
index aee44cb92..158c59df3 100644
--- a/docs/guides/2-provider-configuration.md
+++ b/docs/guides/2-provider-configuration.md
@@ -7,6 +7,8 @@ subcategory: "Guides"
## Example usage
+### API Key
+
`main.tf`
```hcl
@@ -25,13 +27,49 @@ provider "octopusdeploy" {
}
```
+### Access Token (via Environment Variable)
+OIDC Access Tokens are short-lived and typically generated per-run of an automated pipeline, such as GitHub Actions.
+If you use the Access Token approach, we recommend sourcing the token from environment variable.
+
+The environment variable fallback values that the Terraform Provider search for correspond to the values that pipeline steps like our [GitHub Login action](https://github.com/OctopusDeploy/login?tab=readme-ov-file#outputs) set in the pipeline context, so the provider will automatically pick up the value from environment variable.
+
+`main.tf`
+
+```hcl
+terraform {
+ required_providers {
+ octopusdeploy = {
+ source = OctopusDeployLabs/octopusdeploy
+ }
+ }
+}
+
+provider "octopusdeploy" {
+ space_id = "..."
+}
+```
+
## Schema
### Required
-* `address` (String) The Octopus Deploy server URL. This can also be set using the `OCTOPUS_URL` environment variable.
-* `api_key` (String) The Octopus Deploy server API key. This can also be set using the `OCTOPUS_APIKEY` environment variable.
+* `address` (String) The Octopus Deploy server URL.
+
+and one of either
+* `api_key` (String) The Octopus Deploy server API key.
+
+OR
+* `access_token` (String) The OIDC Access Token from an OIDC exchange.
### Optional
* `space_id` (String) The ID of the space to create the resources in.
-**If `space_id` is not specified the default space will be used.**
\ No newline at end of file
+**If `space_id` is not specified the default space will be used.**
+
+### Environment Variable fallback
+The following priority order will be used to calculate the final value for these configuration items:
+
+| Configuration Item | Priority Order |
+|--------------------|--------------------------------------------------------------------------------------------------|
+| `address` | 1. Provider Configuration Block
2. env: `OCTOPUS_URL` |
+| `api_key` | 1. Provider Configuration Block
2. env: `OCTOPUS_APIKEY`
3. env: `OCTOPUS_API_KEY` |
+| `access_token` | 1. Provider Configuration Block
2. env: `OCTOPUS_ACCESS_TOKEN` |
diff --git a/docs/index.md b/docs/index.md
index e0fadfb5f..634c4f849 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -17,6 +17,13 @@ This provider is used to configure resources in Octopus Deploy. The provider mus
## Configuration
+### Authentication Methods
+The provider supports authenticating to an Octopus Server instance via either:
+* API Key
+* OIDC Access Token
+
+These are mutually exclusive options - use either, not both. For backward compatibility, API Key will always be preferred over OIDC, when an API Key is present.
+
### Default Space
Octopus Deploy supports the concept of a Default Space. This is the first space that is automatically created on server setup. If you do not specify a Space when configuring the Octopus Deploy Terraform provider it will use the Default Space.
@@ -81,6 +88,7 @@ resource "octopusdeploy_environment" "Env3" {
### Optional
+- `access_token` (String) The OIDC Access Token to use with the Octopus REST API
- `address` (String) The endpoint of the Octopus REST API
- `api_key` (String) The API key to use with the Octopus REST API
- `space_id` (String) The space ID to target
\ No newline at end of file
diff --git a/octopusdeploy/config.go b/octopusdeploy/config.go
index e084da8eb..037171ed1 100644
--- a/octopusdeploy/config.go
+++ b/octopusdeploy/config.go
@@ -1,6 +1,7 @@
package octopusdeploy
import (
+ "fmt"
"net/url"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
@@ -10,19 +11,15 @@ import (
// Config holds Address and the APIKey of the Octopus Deploy server
type Config struct {
- Address string
- APIKey string
- SpaceID string
+ Address string
+ APIKey string
+ AccessToken string
+ SpaceID string
}
// Client returns a new Octopus Deploy client
func (c *Config) Client() (*client.Client, diag.Diagnostics) {
- apiURL, err := url.Parse(c.Address)
- if err != nil {
- return nil, diag.FromErr(err)
- }
-
- octopus, err := client.NewClient(nil, apiURL, c.APIKey, "")
+ octopus, err := getClientForDefaultSpace(c)
if err != nil {
return nil, diag.FromErr(err)
}
@@ -33,7 +30,7 @@ func (c *Config) Client() (*client.Client, diag.Diagnostics) {
return nil, diag.FromErr(err)
}
- octopus, err = client.NewClient(nil, apiURL, c.APIKey, space.GetID())
+ octopus, err = getClientForSpace(c, space.GetID())
if err != nil {
return nil, diag.FromErr(err)
}
@@ -41,3 +38,43 @@ func (c *Config) Client() (*client.Client, diag.Diagnostics) {
return octopus, nil
}
+
+func getClientForDefaultSpace(c *Config) (*client.Client, error) {
+ return getClientForSpace(c, "")
+}
+
+func getClientForSpace(c *Config, spaceID string) (*client.Client, error) {
+ apiURL, err := url.Parse(c.Address)
+ if err != nil {
+ return nil, err
+ }
+
+ credential, err := getApiCredential(c)
+ if err != nil {
+ return nil, err
+ }
+
+ return client.NewClientWithCredentials(nil, apiURL, credential, spaceID, "TerraformProvider")
+}
+
+func getApiCredential(c *Config) (client.ICredential, error) {
+ if c.APIKey != "" {
+ credential, err := client.NewApiKey(c.APIKey)
+ if err != nil {
+ return nil, err
+ }
+
+ return credential, nil
+ }
+
+ if c.AccessToken != "" {
+ credential, err := client.NewAccessToken(c.AccessToken)
+ if err != nil {
+ return nil, err
+ }
+
+ return credential, nil
+ }
+
+ return nil, fmt.Errorf("either an APIKey or an AccessToken is required to connect to the Octopus Server instance")
+}
diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go
index 5f1a0822f..3b281bfbb 100644
--- a/octopusdeploy/provider.go
+++ b/octopusdeploy/provider.go
@@ -76,11 +76,17 @@ func Provider() *schema.Provider {
Type: schema.TypeString,
},
"api_key": {
- DefaultFunc: schema.EnvDefaultFunc("OCTOPUS_APIKEY", nil),
+ DefaultFunc: schema.MultiEnvDefaultFunc([]string{"OCTOPUS_APIKEY", "OCTOPUS_API_KEY"}, nil),
Description: "The API key to use with the Octopus REST API",
Optional: true,
Type: schema.TypeString,
},
+ "access_token": {
+ DefaultFunc: schema.EnvDefaultFunc("OCTOPUS_ACCESS_TOKEN", nil),
+ Description: "The OIDC Access Token to use with the Octopus REST API",
+ Optional: true,
+ Type: schema.TypeString,
+ },
"space_id": {
Description: "The space ID to target",
Optional: true,
@@ -94,8 +100,9 @@ func Provider() *schema.Provider {
func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
config := Config{
- Address: d.Get("address").(string),
- APIKey: d.Get("api_key").(string),
+ AccessToken: d.Get("access_token").(string),
+ Address: d.Get("address").(string),
+ APIKey: d.Get("api_key").(string),
}
if spaceID, ok := d.GetOk("space_id"); ok {
config.SpaceID = spaceID.(string)
diff --git a/octopusdeploy_framework/config.go b/octopusdeploy_framework/config.go
index 96bf2da70..ef106320d 100644
--- a/octopusdeploy_framework/config.go
+++ b/octopusdeploy_framework/config.go
@@ -12,20 +12,17 @@ import (
)
type Config struct {
- Address string
- ApiKey string
- SpaceID string
- Client *client.Client
+ Address string
+ ApiKey string
+ AccessToken string
+ SpaceID string
+ Client *client.Client
}
func (c *Config) GetClient(ctx context.Context) error {
tflog.Debug(ctx, "GetClient")
- apiURL, err := url.Parse(c.Address)
- if err != nil {
- return err
- }
- octopus, err := client.NewClient(nil, apiURL, c.ApiKey, "")
+ octopus, err := getClientForDefaultSpace(c, ctx)
if err != nil {
return err
}
@@ -36,7 +33,7 @@ func (c *Config) GetClient(ctx context.Context) error {
return err
}
- octopus, err = client.NewClient(nil, apiURL, c.ApiKey, space.GetID())
+ octopus, err = getClientForSpace(c, ctx, space.GetID())
if err != nil {
return err
}
@@ -49,6 +46,54 @@ func (c *Config) GetClient(ctx context.Context) error {
return nil
}
+func getClientForDefaultSpace(c *Config, ctx context.Context) (*client.Client, error) {
+ return getClientForSpace(c, ctx, "")
+}
+
+func getClientForSpace(c *Config, ctx context.Context, spaceID string) (*client.Client, error) {
+ apiURL, err := url.Parse(c.Address)
+ if err != nil {
+ return nil, err
+ }
+
+ credential, err := getApiCredential(c, ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ return client.NewClientWithCredentials(nil, apiURL, credential, spaceID, "TerraformProvider")
+}
+
+func getApiCredential(c *Config, ctx context.Context) (client.ICredential, error) {
+ tflog.Debug(ctx, "GetClient: Trying the following auth methods in order of priority - APIKey, AccessToken")
+
+ if c.ApiKey != "" {
+ tflog.Debug(ctx, "GetClient: Attempting to authenticate with API Key")
+ credential, err := client.NewApiKey(c.ApiKey)
+ if err != nil {
+ return nil, err
+ }
+
+ return credential, nil
+ } else {
+ tflog.Debug(ctx, "GetClient: No API Key found")
+ }
+
+ if c.AccessToken != "" {
+ tflog.Debug(ctx, "GetClient: Attempting to authenticate with Access Token")
+ credential, err := client.NewAccessToken(c.AccessToken)
+ if err != nil {
+ return nil, err
+ }
+
+ return credential, nil
+ } else {
+ tflog.Debug(ctx, "GetClient: No Access Token found")
+ }
+
+ return nil, fmt.Errorf("either an APIKey or an AccessToken is required to connect to the Octopus Server instance")
+}
+
func DataSourceConfiguration(req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) *Config {
if req.ProviderData == nil {
return nil
diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go
index 265eeac96..6cce7bc07 100644
--- a/octopusdeploy_framework/framework_provider.go
+++ b/octopusdeploy_framework/framework_provider.go
@@ -13,9 +13,10 @@ import (
)
type octopusDeployFrameworkProvider struct {
- Address types.String `tfsdk:"address"`
- ApiKey types.String `tfsdk:"api_key"`
- SpaceID types.String `tfsdk:"space_id"`
+ Address types.String `tfsdk:"address"`
+ ApiKey types.String `tfsdk:"api_key"`
+ AccessToken types.String `tfsdk:"access_token"`
+ SpaceID types.String `tfsdk:"space_id"`
}
var _ provider.Provider = (*octopusDeployFrameworkProvider)(nil)
@@ -45,6 +46,12 @@ func (p *octopusDeployFrameworkProvider) Configure(ctx context.Context, req prov
if config.ApiKey == "" {
config.ApiKey = os.Getenv("OCTOPUS_APIKEY")
}
+ if config.ApiKey == "" {
+ config.ApiKey = os.Getenv("OCTOPUS_API_KEY")
+ }
+ if config.AccessToken == "" {
+ config.AccessToken = os.Getenv("OCTOPUS_ACCESS_TOKEN")
+ }
config.Address = providerData.Address.ValueString()
if config.Address == "" {
config.Address = os.Getenv("OCTOPUS_URL")
@@ -118,6 +125,10 @@ func (p *octopusDeployFrameworkProvider) Schema(_ context.Context, req provider.
Optional: true,
Description: "The API key to use with the Octopus REST API",
},
+ "access_token": schema.StringAttribute{
+ Optional: true,
+ Description: "The OIDC Access Token to use with the Octopus REST API",
+ },
"space_id": schema.StringAttribute{
Optional: true,
Description: "The space ID to target",
diff --git a/templates/guides/2-provider-configuration.md.tmpl b/templates/guides/2-provider-configuration.md.tmpl
index aee44cb92..158c59df3 100644
--- a/templates/guides/2-provider-configuration.md.tmpl
+++ b/templates/guides/2-provider-configuration.md.tmpl
@@ -7,6 +7,8 @@ subcategory: "Guides"
## Example usage
+### API Key
+
`main.tf`
```hcl
@@ -25,13 +27,49 @@ provider "octopusdeploy" {
}
```
+### Access Token (via Environment Variable)
+OIDC Access Tokens are short-lived and typically generated per-run of an automated pipeline, such as GitHub Actions.
+If you use the Access Token approach, we recommend sourcing the token from environment variable.
+
+The environment variable fallback values that the Terraform Provider search for correspond to the values that pipeline steps like our [GitHub Login action](https://github.com/OctopusDeploy/login?tab=readme-ov-file#outputs) set in the pipeline context, so the provider will automatically pick up the value from environment variable.
+
+`main.tf`
+
+```hcl
+terraform {
+ required_providers {
+ octopusdeploy = {
+ source = OctopusDeployLabs/octopusdeploy
+ }
+ }
+}
+
+provider "octopusdeploy" {
+ space_id = "..."
+}
+```
+
## Schema
### Required
-* `address` (String) The Octopus Deploy server URL. This can also be set using the `OCTOPUS_URL` environment variable.
-* `api_key` (String) The Octopus Deploy server API key. This can also be set using the `OCTOPUS_APIKEY` environment variable.
+* `address` (String) The Octopus Deploy server URL.
+
+and one of either
+* `api_key` (String) The Octopus Deploy server API key.
+
+OR
+* `access_token` (String) The OIDC Access Token from an OIDC exchange.
### Optional
* `space_id` (String) The ID of the space to create the resources in.
-**If `space_id` is not specified the default space will be used.**
\ No newline at end of file
+**If `space_id` is not specified the default space will be used.**
+
+### Environment Variable fallback
+The following priority order will be used to calculate the final value for these configuration items:
+
+| Configuration Item | Priority Order |
+|--------------------|--------------------------------------------------------------------------------------------------|
+| `address` | 1. Provider Configuration Block
2. env: `OCTOPUS_URL` |
+| `api_key` | 1. Provider Configuration Block
2. env: `OCTOPUS_APIKEY`
3. env: `OCTOPUS_API_KEY` |
+| `access_token` | 1. Provider Configuration Block
2. env: `OCTOPUS_ACCESS_TOKEN` |
diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl
index 16c88cd8d..2f907216f 100644
--- a/templates/index.md.tmpl
+++ b/templates/index.md.tmpl
@@ -17,6 +17,13 @@ This provider is used to configure resources in Octopus Deploy. The provider mus
## Configuration
+### Authentication Methods
+The provider supports authenticating to an Octopus Server instance via either:
+* API Key
+* OIDC Access Token
+
+These are mutually exclusive options - use either, not both. For backward compatibility, API Key will always be preferred over OIDC, when an API Key is present.
+
### Default Space
Octopus Deploy supports the concept of a Default Space. This is the first space that is automatically created on server setup. If you do not specify a Space when configuring the Octopus Deploy Terraform provider it will use the Default Space.