-
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.
Merge pull request #1627 from hashicorp/ephemeral-agent-token
feat: add tfe_agent_token ephemeral resource
- Loading branch information
Showing
6 changed files
with
255 additions
and
1 deletion.
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
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
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,120 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package provider | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
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 = &AgentTokenEphemeralResource{} | ||
) | ||
|
||
func NewAgentTokenEphemeralResource() ephemeral.EphemeralResource { | ||
return &AgentTokenEphemeralResource{} | ||
} | ||
|
||
type AgentTokenEphemeralResource struct { | ||
config ConfiguredClient | ||
} | ||
|
||
type AgentTokenEphemeralResourceModel struct { | ||
AgentPoolID types.String `tfsdk:"agent_pool_id"` | ||
Description types.String `tfsdk:"description"` | ||
Token types.String `tfsdk:"token"` | ||
} | ||
|
||
// defines a schema describing what data is available in the ephemeral resource's configuration and result data. | ||
func (e *AgentTokenEphemeralResource) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Description: "This ephemeral resource can be used to retrieve an agent token without saving its value in state.", | ||
Attributes: map[string]schema.Attribute{ | ||
"agent_pool_id": schema.StringAttribute{ | ||
Description: `ID of the agent. If omitted, agent must be defined in the provider config.`, | ||
Required: true, | ||
}, | ||
"description": schema.StringAttribute{ | ||
Description: `Description of the agent token.`, | ||
Required: true, | ||
}, | ||
"token": schema.StringAttribute{ | ||
Description: `The generated token.`, | ||
Computed: true, | ||
Sensitive: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// Configure adds the provider configured client to the data source. | ||
func (e *AgentTokenEphemeralResource) 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 *AgentTokenEphemeralResource) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_agent_token" // tfe_agent_token | ||
} | ||
|
||
// The request contains the configuration supplied to Terraform for the ephemeral resource. The response contains the ephemeral result data. The data is defined by the schema of the ephemeral resource. | ||
func (e *AgentTokenEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) { | ||
var data AgentTokenEphemeralResourceModel | ||
|
||
// Read Terraform config data into the model | ||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
agentPoolID := data.AgentPoolID.ValueString() | ||
description := data.Description.ValueString() | ||
|
||
options := tfe.AgentTokenCreateOptions{ | ||
Description: tfe.String(description), | ||
} | ||
|
||
log.Printf("[DEBUG] Create new agent token for agent pool ID: %s", agentPoolID) | ||
log.Printf("[DEBUG] Create new agent token with description: %s", description) | ||
|
||
result, err := e.config.Client.AgentTokens.Create(ctx, agentPoolID, options) | ||
|
||
if err != nil { | ||
resp.Diagnostics.AddError("Unable to create agent token", err.Error()) | ||
return | ||
} | ||
|
||
data = ephemeralResourceModelFromTFEagentToken(agentPoolID, result) | ||
|
||
// Save to ephemeral result data | ||
resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...) | ||
} | ||
|
||
// ephemeralResourceModelFromTFEagentToken builds a agentTokenEphemeralResourceModel struct from a | ||
// tfe.agentToken value. | ||
func ephemeralResourceModelFromTFEagentToken(id string, v *tfe.AgentToken) AgentTokenEphemeralResourceModel { | ||
return AgentTokenEphemeralResourceModel{ | ||
AgentPoolID: types.StringValue(id), | ||
Description: types.StringValue(v.Description), | ||
Token: types.StringValue(v.Token), | ||
} | ||
} |
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,66 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package provider | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"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 TestAccagentTokenEphemeralResource_basic(t *testing.T) { | ||
tfeClient, err := getClientUsingEnv() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
org, orgCleanup := createBusinessOrganization(t, tfeClient) | ||
t.Cleanup(orgCleanup) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(tfversion.Version1_10_0), | ||
}, | ||
ProtoV5ProviderFactories: testAccMuxedProviders, | ||
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ | ||
"echo": echoprovider.NewProviderServer(), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAgentTokenEphemeralResourceConfig(org.Name), | ||
ConfigStateChecks: []statecheck.StateCheck{ | ||
statecheck.ExpectKnownValue("echo.this", tfjsonpath.New("data"), knownvalue.StringRegexp(regexp.MustCompile(`^[a-zA-Z0-9]+\.atlasv1\.[a-zA-Z0-9]+$`))), | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccAgentTokenEphemeralResourceConfig(orgName string) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_agent_pool" "foobar" { | ||
name = "agent-pool-test" | ||
organization = "%s" | ||
} | ||
ephemeral "tfe_agent_token" "this" { | ||
agent_pool_id = tfe_agent_pool.foobar.id | ||
description = "agent-token-test" | ||
} | ||
provider "echo" { | ||
data = ephemeral.tfe_agent_token.this.token | ||
} | ||
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
59 changes: 59 additions & 0 deletions
59
website/docs/ephemeral-resources/agent_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,59 @@ | ||
--- | ||
layout: "tfe" | ||
page_title: "Terraform Enterprise: tfe_agent_token" | ||
description: |- | ||
Generates an ephemeral agent token. | ||
--- | ||
|
||
# tfe_agent_token | ||
|
||
Generates a new agent token as an ephemeral value. | ||
|
||
Each agent pool can have multiple tokens and they can be long-lived. For that reason, this ephemeral resource does not implement the Close method, which would tear the token down after the configuration is complete. | ||
|
||
Agent token strings are sensitive and only returned on creation, so making those strings ephemeral values is beneficial to avoid state exposure. | ||
|
||
If you need to use this value in the future, make sure to capture the token and save it in a secure location. Any resource with write-only values can accept ephemeral resource attributes. | ||
|
||
## Example Usage | ||
|
||
Basic usage: | ||
|
||
```hcl | ||
ephemeral "tfe_agent_token" "this" { | ||
agent_pool_id = tfe_agent_pool.foobar.id | ||
description = "my description" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `agent_pool_id` - (Required) Id for the Agent Pool. | ||
* `description` - (Required) A brief description about the Agent Pool. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "tfe_agent_pool" "foobar" { | ||
name = "agent-pool-test" | ||
organization = "my-org-name" | ||
} | ||
ephemeral "tfe_agent_token" "this" { | ||
agent_pool_id = tfe_agent_pool.foobar.id | ||
description = "my description" | ||
} | ||
output "my-agent-token" { | ||
value = ephemeral.tfe_agent_token.this.token | ||
description = "Token for tfe agent." | ||
ephemeral = true | ||
} | ||
``` | ||
|
||
## Attributes Reference | ||
|
||
* `token` - The generated token. | ||
|