Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ FEATURES:
* **New resource**: `r/tfe_vault_oidc_configuration` for managing Vault OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
* **New resource**: `r/tfe_aws_oidc_configuration` for managing AWS OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
* **New resource**: `r/tfe_gcp_oidc_configuration` for managing GCP OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
* **New resource**: `r/tfe_azure_oidc_configuration` for managing Azure OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
* **New resource**: `r/tfe_azure_oidc_configuration` for managing Azure OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
* **New resource**: `r/tfe_hyok_configuration` for managing HYOK configurations, by @helenjw. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1841)

## v0.69.0

Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider_next.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func (p *frameworkProvider) Resources(ctx context.Context) []func() resource.Res
NewGCPOIDCConfigurationResource,
NewAzureOIDCConfigurationResource,
NewVaultOIDCConfigurationResource,
NewHYOKConfigurationResource,
}
}

Expand Down
348 changes: 348 additions & 0 deletions internal/provider/resource_tfe_hyok_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
// // Copyright (c) HashiCorp, Inc.
// // SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"

tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

var (
_ resource.ResourceWithConfigure = &resourceTFEHYOKConfiguration{}
_ resource.ResourceWithImportState = &resourceTFEHYOKConfiguration{}
)

func NewHYOKConfigurationResource() resource.Resource {
return &resourceTFEHYOKConfiguration{}
}

type resourceTFEHYOKConfiguration struct {
config ConfiguredClient
}

type modelTFEHYOKConfiguration struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
KEKID types.String `tfsdk:"kek_id"`
KMSOptions *modelTFEKMSOptions `tfsdk:"kms_options"`
OIDCConfigurationID types.String `tfsdk:"oidc_configuration_id"`
OIDCConfigurationType types.String `tfsdk:"oidc_configuration_type"`
AgentPoolID types.String `tfsdk:"agent_pool_id"`
Organization types.String `tfsdk:"organization"`
}

func (m *modelTFEHYOKConfiguration) TFEOIDCConfigurationTypeChoice() *tfe.OIDCConfigurationTypeChoice {

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / lint

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd amd64 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd arm build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows 386 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux 386 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd 386 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin amd64 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin arm64 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm64 build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm build

undefined: tfe.OIDCConfigurationTypeChoice

Check failure on line 48 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows amd64 build

undefined: tfe.OIDCConfigurationTypeChoice
var typeChoice *tfe.OIDCConfigurationTypeChoice
id := m.OIDCConfigurationID.ValueString()

switch m.OIDCConfigurationType.ValueString() {
case OIDCConfigurationTypeAWS:
typeChoice = &tfe.OIDCConfigurationTypeChoice{AWSOIDCConfiguration: &tfe.AWSOIDCConfiguration{ID: id}}
case OIDCConfigurationTypeGCP:
typeChoice = &tfe.OIDCConfigurationTypeChoice{GCPOIDCConfiguration: &tfe.GCPOIDCConfiguration{ID: id}}
case OIDCConfigurationTypeVault:
typeChoice = &tfe.OIDCConfigurationTypeChoice{VaultOIDCConfiguration: &tfe.VaultOIDCConfiguration{ID: id}}
case OIDCConfigurationTypeAzure:
typeChoice = &tfe.OIDCConfigurationTypeChoice{AzureOIDCConfiguration: &tfe.AzureOIDCConfiguration{ID: id}}
}

return typeChoice
}

Choose a reason for hiding this comment

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

What was the reasoning behind using AWSOIDCConfigurationID, GCPOIDCConfigurationID, VaultOIDCConfigurationID, and AzureOIDCConfigurationID

instead of

oidc_configuration_id and oidc_configuration_type like the HyokConfigurations model and database schema uses?

Copy link
Author

Choose a reason for hiding this comment

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

That's also a valid way to approach it. At the end of the day, it just needs to populate the tfe.OIDCConfigurationTypeChoice object that looks like:

type OIDCConfigurationTypeChoice struct {
	AWSOIDCConfiguration   *AWSOIDCConfiguration
	GCPOIDCConfiguration   *GCPOIDCConfiguration
	AzureOIDCConfiguration *AzureOIDCConfiguration
	VaultOIDCConfiguration *VaultOIDCConfiguration
}

I don't really have strong feelings about this, but I think having an explicit type could shorten the code a little.

Choose a reason for hiding this comment

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

Yeah this works well, I was just curious if there was a technical reason or constraint. Thanks!


type modelTFEKMSOptions struct {
KeyRegion types.String `tfsdk:"key_region"`
KeyLocation types.String `tfsdk:"key_location"`
KeyRingID types.String `tfsdk:"key_ring_id"`
}

func (m *modelTFEKMSOptions) TFEKMSOptions() *tfe.KMSOptions {

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / lint

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd amd64 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd arm build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows 386 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux 386 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd 386 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin amd64 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin arm64 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm64 build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm build

undefined: tfe.KMSOptions

Check failure on line 72 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows amd64 build

undefined: tfe.KMSOptions
var kmsOptions *tfe.KMSOptions
if m != nil {
kmsOptions = &tfe.KMSOptions{
KeyRegion: m.KeyRegion.ValueString(),
KeyLocation: m.KeyLocation.ValueString(),
KeyRingID: m.KeyRingID.ValueString(),
}
}
return kmsOptions
}

// List all available OIDC configuration types.
const (
OIDCConfigurationTypeAWS string = "aws"
OIDCConfigurationTypeGCP string = "gcp"
OIDCConfigurationTypeVault string = "vault"
OIDCConfigurationTypeAzure string = "azure"
)

func (r *resourceTFEHYOKConfiguration) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(ConfiguredClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected 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),
)
}
r.config = client
}

func (r *resourceTFEHYOKConfiguration) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_hyok_configuration"
}

func (r *resourceTFEHYOKConfiguration) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The ID of the HYOK configuration.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"name": schema.StringAttribute{
Description: "Label for the HYOK configuration to be used within HCP Terraform.",
Required: true,
},
"kek_id": schema.StringAttribute{
Description: "Refers to the name of your key encryption key stored in your key management service.",
Required: true,
},
"oidc_configuration_id": schema.StringAttribute{
Description: "The ID of the TFE OIDC configuration.",
Required: true,
},
"oidc_configuration_type": schema.StringAttribute{
Description: "The type of the TFE OIDC configuration.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
stringvalidator.OneOf(
string(OIDCConfigurationTypeAWS),
string(OIDCConfigurationTypeGCP),
string(OIDCConfigurationTypeVault),
string(OIDCConfigurationTypeAzure),
),
},
},
"agent_pool_id": schema.StringAttribute{
Description: "The ID of the agent-pool to associate with the HYOK configuration.",
Required: true,
},
"organization": schema.StringAttribute{
Description: "Name of the organization to which the TFE HYOK configuration belongs.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
Blocks: map[string]schema.Block{
"kms_options": schema.SingleNestedBlock{
Description: "Optional object used to specify additional fields for some key management services.",
Attributes: map[string]schema.Attribute{
"key_region": schema.StringAttribute{
Description: "The AWS region where your key is located.",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
},
"key_location": schema.StringAttribute{
Description: "The location in which the GCP key ring exists.",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
},
"key_ring_id": schema.StringAttribute{
Description: "The root resource for Google Cloud KMS keys and key versions.",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
},
},
},
},
Description: "Generates a new TFE HYOK Configuration.",
}
}

func (r *resourceTFEHYOKConfiguration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

func (r *resourceTFEHYOKConfiguration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Read Terraform plan into the model
var plan modelTFEHYOKConfiguration
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// Get the organization name from resource or provider config
var orgName string
resp.Diagnostics.Append(r.config.dataOrDefaultOrganization(ctx, req.Config, &orgName)...)
if resp.Diagnostics.HasError() {
return
}

options := tfe.HYOKConfigurationsCreateOptions{
KEKID: plan.KEKID.ValueString(),
Name: plan.Name.ValueString(),
KMSOptions: plan.KMSOptions.TFEKMSOptions(),
OIDCConfiguration: plan.TFEOIDCConfigurationTypeChoice(),
AgentPool: &tfe.AgentPool{ID: plan.AgentPoolID.ValueString()},
}

tflog.Debug(ctx, fmt.Sprintf("Create TFE HYOK Configuration for organization %s", orgName))
hyok, err := r.config.Client.HYOKConfigurations.Create(ctx, orgName, options)
if err != nil {
resp.Diagnostics.AddError("Error creating TFE HYOK Configuration", err.Error())
return
}
result := modelFromTFEHYOKConfiguration(hyok)
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
}

func (r *resourceTFEHYOKConfiguration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
// Read Terraform state into the model
var state modelTFEHYOKConfiguration
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

hyokID := state.ID.ValueString()
opts := tfe.HYOKConfigurationsReadOptions{
Include: []tfe.HYOKConfigurationsIncludeOpt{
tfe.HYOKConfigurationsIncludeOIDCConfiguration,
},
}
tflog.Debug(ctx, fmt.Sprintf("Read HYOK configuration: %s", hyokID))
hyok, err := r.config.Client.HYOKConfigurations.Read(ctx, state.ID.ValueString(), &opts)
if err != nil {
if errors.Is(err, tfe.ErrResourceNotFound) {
tflog.Debug(ctx, fmt.Sprintf("HYOK configuration %s no longer exists", hyokID))
resp.State.RemoveResource(ctx)
} else {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading HYOK configuration %s", hyokID),
err.Error(),
)
}
return
}
result := modelFromTFEHYOKConfiguration(hyok)
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
}

func (r *resourceTFEHYOKConfiguration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan modelTFEHYOKConfiguration
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)

var state modelTFEHYOKConfiguration
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

options := tfe.HYOKConfigurationsUpdateOptions{
Name: plan.Name.ValueStringPointer(),
KEKID: plan.KEKID.ValueStringPointer(),
KMSOptions: plan.KMSOptions.TFEKMSOptions(),
AgentPool: &tfe.AgentPool{ID: plan.AgentPoolID.ValueString()},
}

hyokID := state.ID.ValueString()
tflog.Debug(ctx, fmt.Sprintf("Update TFE HYOK Configuration %s", hyokID))
hyok, err := r.config.Client.HYOKConfigurations.Update(ctx, hyokID, options)
if err != nil {
resp.Diagnostics.AddError("Error updating TFE HYOK Configuration", err.Error())
return
}

result := modelFromTFEHYOKConfiguration(hyok)
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
}

func (r *resourceTFEHYOKConfiguration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state modelTFEHYOKConfiguration
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

hyokID := state.ID.ValueString()
tflog.Debug(ctx, fmt.Sprintf("Delete TFE HYOK configuration: %s", hyokID))
err := r.config.Client.HYOKConfigurations.Delete(ctx, hyokID)
if err != nil {
if errors.Is(err, tfe.ErrResourceNotFound) {
tflog.Debug(ctx, fmt.Sprintf("TFE HYOK configuration %s no longer exists", hyokID))
return
}

resp.Diagnostics.AddError("Error deleting TFE HYOK Configuration", err.Error())
return
}
}

func modelFromTFEHYOKConfiguration(p *tfe.HYOKConfiguration) modelTFEHYOKConfiguration {

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / lint

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd amd64 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd arm build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows 386 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux 386 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 freebsd 386 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin amd64 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 darwin arm64 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm64 build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 linux arm build

undefined: tfe.HYOKConfiguration

Check failure on line 314 in internal/provider/resource_tfe_hyok_configuration.go

View workflow job for this annotation

GitHub Actions / Go 1.24.0 windows amd64 build

undefined: tfe.HYOKConfiguration
var kmsOptions *modelTFEKMSOptions
if p.KMSOptions != nil {
kmsOptions = &modelTFEKMSOptions{
KeyRegion: types.StringValue(p.KMSOptions.KeyRegion),
KeyLocation: types.StringValue(p.KMSOptions.KeyLocation),
KeyRingID: types.StringValue(p.KMSOptions.KeyRingID),
}
}

model := modelTFEHYOKConfiguration{
ID: types.StringValue(p.ID),
Name: types.StringValue(p.Name),
KEKID: types.StringValue(p.KEKID),
Organization: types.StringValue(p.Organization.Name),
AgentPoolID: types.StringValue(p.AgentPool.ID),
KMSOptions: kmsOptions,
}

if p.OIDCConfiguration.AWSOIDCConfiguration != nil {

Choose a reason for hiding this comment

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

since were doing a switch-case on the top of the file, I'd suggest we do a switch-case here as well?

model.OIDCConfigurationID = types.StringValue(p.OIDCConfiguration.AWSOIDCConfiguration.ID)
model.OIDCConfigurationType = types.StringValue(OIDCConfigurationTypeAWS)
} else if p.OIDCConfiguration.GCPOIDCConfiguration != nil {
model.OIDCConfigurationID = types.StringValue(p.OIDCConfiguration.GCPOIDCConfiguration.ID)
model.OIDCConfigurationType = types.StringValue(OIDCConfigurationTypeGCP)
} else if p.OIDCConfiguration.AzureOIDCConfiguration != nil {
model.OIDCConfigurationID = types.StringValue(p.OIDCConfiguration.AzureOIDCConfiguration.ID)
model.OIDCConfigurationType = types.StringValue(OIDCConfigurationTypeAzure)
} else if p.OIDCConfiguration.VaultOIDCConfiguration != nil {
model.OIDCConfigurationID = types.StringValue(p.OIDCConfiguration.VaultOIDCConfiguration.ID)
model.OIDCConfigurationType = types.StringValue(OIDCConfigurationTypeVault)
}

return model
}
Loading
Loading