-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add tfe_organization_token ephemeral resource
- Loading branch information
Showing
5 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
internal/provider/ephemeral_resource_organization_token.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package provider | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-framework/ephemeral" | ||
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
var ( | ||
_ ephemeral.EphemeralResource = &OrganizationTokenEphemeralResource{} | ||
_ ephemeral.EphemeralResourceWithConfigure = &OrganizationTokenEphemeralResource{} | ||
) | ||
|
||
func NewOrganizationTokenEphemeralResource() ephemeral.EphemeralResource { | ||
return &OrganizationTokenEphemeralResource{} | ||
} | ||
|
||
type OrganizationTokenEphemeralResource struct { | ||
config ConfiguredClient | ||
} | ||
|
||
type OrganizationTokenEphemeralResourceModel struct { | ||
Organization types.String `tfsdk:"organization"` | ||
ExpiredAt types.String `tfsdk:"expired_at"` | ||
Token types.String `tfsdk:"token"` | ||
} | ||
|
||
func (e *OrganizationTokenEphemeralResource) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Description: "This ephemeral resource can be used to retrieve an organization token without saving its value in state. Using this ephemeral resource will generate a new token each time it is used, invalidating any existing organization token.", | ||
Attributes: map[string]schema.Attribute{ | ||
"organization": schema.StringAttribute{ | ||
Description: `Name of the organization. If omitted, organization must be defined in the provider config.`, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"expired_at": schema.StringAttribute{ | ||
Description: `The token's expiration date. The expiration date must be a date/time string in RFC3339 format (e.g., "2024-12-31T23:59:59Z"). If no expiration date is supplied, the expiration date will default to null and never expire.`, | ||
Optional: true, | ||
}, | ||
"token": schema.StringAttribute{ | ||
Description: `The generated token.`, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// Configure adds the provider configured client to the data source. | ||
func (e *OrganizationTokenEphemeralResource) Configure(_ context.Context, req ephemeral.ConfigureRequest, resp *ephemeral.ConfigureResponse) { | ||
if req.ProviderData == nil { | ||
return | ||
} | ||
|
||
client, ok := req.ProviderData.(ConfiguredClient) | ||
if !ok { | ||
resp.Diagnostics.AddError( | ||
"Unexpected Ephemeral Resource Configure Type", | ||
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData), | ||
) | ||
|
||
return | ||
} | ||
|
||
e.config = client | ||
} | ||
|
||
func (e *OrganizationTokenEphemeralResource) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_organization_token" | ||
} | ||
|
||
func (e *OrganizationTokenEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) { | ||
// Read Terraform config config | ||
var config OrganizationTokenEphemeralResourceModel | ||
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
// Get org name or default | ||
var orgName string | ||
resp.Diagnostics.Append(e.config.dataOrDefaultOrganization(ctx, req.Config, &orgName)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
// Create options | ||
var expiredAt *time.Time | ||
if !config.ExpiredAt.IsNull() { | ||
parsed, err := time.Parse(time.RFC3339, config.ExpiredAt.String()) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Invalid expired_at value", err.Error()) | ||
return | ||
} | ||
|
||
expiredAt = &parsed | ||
} | ||
|
||
opts := tfe.OrganizationTokenCreateOptions{ | ||
ExpiredAt: expiredAt, | ||
} | ||
|
||
// Create a new token | ||
result, err := e.config.Client.OrganizationTokens.CreateWithOptions(ctx, orgName, opts) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Unable to create organization token", err.Error()) | ||
return | ||
} | ||
|
||
// Set the token in the model | ||
config.Token = types.StringValue(result.Token) | ||
|
||
// Write the data back to the ephemeral resource | ||
resp.Diagnostics.Append(resp.Result.Set(ctx, &config)...) | ||
} |
106 changes: 106 additions & 0 deletions
106
internal/provider/ephemeral_resource_organization_token_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package provider | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-go/tfprotov6" | ||
"github.com/hashicorp/terraform-plugin-testing/echoprovider" | ||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-testing/knownvalue" | ||
"github.com/hashicorp/terraform-plugin-testing/statecheck" | ||
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath" | ||
"github.com/hashicorp/terraform-plugin-testing/tfversion" | ||
) | ||
|
||
func TestAccOrganizationTokenEphemeralResource_basic(t *testing.T) { | ||
tfeClient, err := getClientUsingEnv() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
org, orgCleanup := createBusinessOrganization(t, tfeClient) | ||
t.Cleanup(orgCleanup) | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(tfversion.Version1_10_0), | ||
}, | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProtoV5ProviderFactories: testAccMuxedProviders, | ||
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ | ||
"echo": echoprovider.NewProviderServer(), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccOrganizationTokenEphemeralResourceConfig_basic(org.Name), | ||
ConfigStateChecks: []statecheck.StateCheck{ | ||
statecheck.ExpectKnownValue("echo.this", tfjsonpath.New("data"), knownvalue.StringExact(org.Name)), | ||
}, | ||
RefreshState: false, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccOrganizationTokenEphemeralResource_expiredAt(t *testing.T) { | ||
tfeClient, err := getClientUsingEnv() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
org, orgCleanup := createBusinessOrganization(t, tfeClient) | ||
t.Cleanup(orgCleanup) | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(tfversion.Version1_10_0), | ||
}, | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProtoV5ProviderFactories: testAccMuxedProviders, | ||
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ | ||
"echo": echoprovider.NewProviderServer(), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccOrganizationTokenEphemeralResourceConfig_expiredAt(org.Name), | ||
ConfigStateChecks: []statecheck.StateCheck{ | ||
statecheck.ExpectKnownValue("echo.this", tfjsonpath.New("data"), knownvalue.StringExact("2100-01-01T00:00:00Z")), | ||
}, | ||
RefreshState: false, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccOrganizationTokenEphemeralResourceConfig_basic(orgName string) string { | ||
return fmt.Sprintf(` | ||
ephemeral "tfe_organization_token" "this" { | ||
organization = "%s" | ||
} | ||
provider "echo" { | ||
data = ephemeral.tfe_organization_token.this.organization | ||
} | ||
resource "echo" "this" {} | ||
`, orgName) | ||
} | ||
|
||
func testAccOrganizationTokenEphemeralResourceConfig_expiredAt(orgName string) string { | ||
return fmt.Sprintf(` | ||
ephemeral "tfe_organization_token" "this" { | ||
organization = "%s" | ||
expired_at = "2100-01-01T00:00:00Z" | ||
} | ||
provider "echo" { | ||
data = ephemeral.tfe_organization_token.this.expired_at | ||
} | ||
resource "echo" "this" {} | ||
`, orgName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
website/docs/ephemeral-resources/organization_token.html.markdown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
--- | ||
layout: "tfe" | ||
page_title: "Terraform Enterprise: Ephemeral: tfe_organization_token" | ||
description: |- | ||
Generates a new organization token that is guaranteed not to be written to | ||
state. | ||
--- | ||
|
||
# Ephemeral: tfe_organization_token | ||
|
||
Terraform ephemeral resource for managing a TFE organization token. This | ||
resource is used to generate a new organization token that is guaranteed not to | ||
be written to state. Since organization tokens are singleton resources, using this ephemeral resource will replace any existing organization token. | ||
|
||
~> **NOTE:** Ephemeral resources are a new feature and may evolve as we continue to explore their most effective uses. [Learn more](https://developer.hashicorp.com/terraform/language/v1.10.x/resources/ephemeral). | ||
|
||
## Example Usage | ||
|
||
### Generate a new organization token: | ||
|
||
This will invalidate any existing organization token. | ||
|
||
```hcl | ||
resource "tfe_organization_token" "example" { | ||
organization = "my-org-name" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are required: | ||
|
||
* `organization` - (Required) Name of the organization. If omitted, organization must be defined in the provider config. | ||
|
||
The following arguments are optional: | ||
|
||
* `expired_at` - (Optional) The token's expiration date. The expiration date must be a date/time string in RFC3339 | ||
format (e.g., "2024-12-31T23:59:59Z"). If no expiration date is supplied, the expiration date will default to null and | ||
never expire. | ||
|
||
This ephemeral resource exports the following attributes in addition to the arguments above: | ||
|
||
* `token` - The generated token. This value is sensitive and will not be stored | ||
in state. |