Skip to content

Commit 8a2e80c

Browse files
Merge branch 'feature/hyok' into dominicretli/TF-28674/hyok-data-objects
2 parents a7622dd + c83b4ba commit 8a2e80c

19 files changed

+2230
-4
lines changed

CHANGELOG.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
## Unreleased
22
FEATURES:
3-
* **New Data Source:** `d/hyok_customer_key_version` is a new data source for finding
4-
HYOK customer key versions by @dominicretli [#1842](https://github.com/hashicorp/terraform-provider-tfe/pull/1842)
5-
* **New Data Source:** `d/hyok_encrypted_data_key` is a new data source for finding
6-
HYOK encrypted data keys by @dominicretli [#1842](https://github.com/hashicorp/terraform-provider-tfe/pull/1842)
3+
* **New resource**: `r/tfe_vault_oidc_configuration` for managing Vault OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
4+
* **New resource**: `r/tfe_aws_oidc_configuration` for managing AWS OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
5+
* **New resource**: `r/tfe_gcp_oidc_configuration` for managing GCP OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
6+
* **New resource**: `r/tfe_azure_oidc_configuration` for managing Azure OIDC configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1835)
7+
* **New resource**: `r/tfe_hyok_configuration` for managing HYOK configurations. [#1835](https://github.com/hashicorp/terraform-provider-tfe/pull/1841)
8+
* **New Data Source:** `d/hyok_customer_key_version` is a new data source for finding HYOK customer key versions by @dominicretli [#1842](https://github.com/hashicorp/terraform-provider-tfe/pull/1842)
9+
* **New Data Source:** `d/hyok_encrypted_data_key` is a new data source for finding HYOK encrypted data keys by @dominicretli [#1842](https://github.com/hashicorp/terraform-provider-tfe/pull/1842)
710

811
## v0.70.0
912

docs/testing.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ these values with the environment variables specified below:
4949
1. `RUN_TASKS_HMAC` - The optional HMAC Key that should be used for Run Task operations. The default is no key.
5050
1. `GITHUB_APP_INSTALLATION_ID` - GitHub App installation internal id in the format `ghain-xxxxxxx`. Required for running any tests that use GitHub App VCS (workspace, policy sets, registry module).
5151
1. `GITHUB_APP_INSTALLATION_NAME` - GitHub App installation name. Required for running tfe_github_app_installation data source test.
52+
1. `ENABLE_HYOK` - Set `ENABLE_HYOK=1` to enable HYOK-related tests.
53+
1. `HYOK_ORGANIZATION_NAME` - Name of an organization entitled to use HYOK. Required to run tests for HYOK resources and data sources.
5254
1. `HYOK_ENCRYPTED_DATA_KEY_ID` - HYOK encrypted data key id. Required for running hyok_encrypted_data_key data source test.
5355
1. `HYOK_CUSTOMER_KEY_VERSION_ID` - HYOK customer key version id. Required for running hyok_customer_key_version data source test.
5456

internal/provider/helper_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
const RunTasksURLEnvName = "RUN_TASKS_URL"
2020
const RunTasksHMACKeyEnvName = "RUN_TASKS_HMAC"
21+
const EnableHYOKEnvName = "ENABLE_HYOK"
2122

2223
type testClientOptions struct {
2324
defaultOrganization string
@@ -238,6 +239,14 @@ func skipUnlessBeta(t *testing.T) {
238239
}
239240
}
240241

242+
func skipUnlessHYOKEnabled(t *testing.T) {
243+
skipIfEnterprise(t)
244+
245+
if value, ok := os.LookupEnv(EnableHYOKEnvName); !ok || value == "" {
246+
t.Skipf("Skipping tests for HYOK. Set '%s' to enable tests.", EnableHYOKEnvName)
247+
}
248+
}
249+
241250
// Temporarily skip a test that may be experiencing API errors. This method
242251
// purposefully errors after the set date to remind contributors to remove this check
243252
// and verify that the API errors are no longer occurring.

internal/provider/provider_next.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ func (p *frameworkProvider) Resources(ctx context.Context) []func() resource.Res
174174
NewTerraformVersionResource,
175175
NewOPAVersionResource,
176176
NewsentinelVersionResource,
177+
NewAWSOIDCConfigurationResource,
178+
NewGCPOIDCConfigurationResource,
179+
NewAzureOIDCConfigurationResource,
180+
NewVaultOIDCConfigurationResource,
181+
NewHYOKConfigurationResource,
177182
}
178183
}
179184

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// // Copyright (c) HashiCorp, Inc.
2+
// // SPDX-License-Identifier: MPL-2.0
3+
4+
package provider
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
"github.com/hashicorp/terraform-plugin-framework/path"
11+
12+
tfe "github.com/hashicorp/go-tfe"
13+
"github.com/hashicorp/terraform-plugin-framework/resource"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/types"
18+
"github.com/hashicorp/terraform-plugin-log/tflog"
19+
)
20+
21+
var (
22+
_ resource.ResourceWithConfigure = &resourceTFEAWSOIDCConfiguration{}
23+
_ resource.ResourceWithImportState = &resourceTFEAWSOIDCConfiguration{}
24+
)
25+
26+
func NewAWSOIDCConfigurationResource() resource.Resource {
27+
return &resourceTFEAWSOIDCConfiguration{}
28+
}
29+
30+
type resourceTFEAWSOIDCConfiguration struct {
31+
config ConfiguredClient
32+
}
33+
34+
type modelTFEAWSOIDCConfiguration struct {
35+
ID types.String `tfsdk:"id"`
36+
RoleARN types.String `tfsdk:"role_arn"`
37+
Organization types.String `tfsdk:"organization"`
38+
}
39+
40+
func (r *resourceTFEAWSOIDCConfiguration) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
41+
// Prevent panic if the provider has not been configured.
42+
if req.ProviderData == nil {
43+
return
44+
}
45+
46+
client, ok := req.ProviderData.(ConfiguredClient)
47+
if !ok {
48+
resp.Diagnostics.AddError(
49+
"Unexpected resource Configure type",
50+
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData),
51+
)
52+
}
53+
r.config = client
54+
}
55+
56+
func (r *resourceTFEAWSOIDCConfiguration) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
57+
resp.TypeName = req.ProviderTypeName + "_aws_oidc_configuration"
58+
}
59+
60+
func (r *resourceTFEAWSOIDCConfiguration) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
61+
resp.Schema = schema.Schema{
62+
Attributes: map[string]schema.Attribute{
63+
"id": schema.StringAttribute{
64+
Description: "The ID of the AWS OIDC configuration.",
65+
Computed: true,
66+
PlanModifiers: []planmodifier.String{
67+
stringplanmodifier.UseStateForUnknown(),
68+
},
69+
},
70+
"role_arn": schema.StringAttribute{
71+
Description: "The AWS ARN of your role.",
72+
Required: true,
73+
},
74+
"organization": schema.StringAttribute{
75+
Description: "Name of the organization to which the TFE AWS OIDC configuration belongs.",
76+
Optional: true,
77+
Computed: true,
78+
PlanModifiers: []planmodifier.String{
79+
stringplanmodifier.RequiresReplace(),
80+
},
81+
},
82+
},
83+
Description: "Generates a new TFE AWS OIDC Configuration.",
84+
}
85+
}
86+
87+
func (r *resourceTFEAWSOIDCConfiguration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
88+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
89+
}
90+
91+
func (r *resourceTFEAWSOIDCConfiguration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
92+
// Read Terraform plan into the model
93+
var plan modelTFEAWSOIDCConfiguration
94+
diags := req.Plan.Get(ctx, &plan)
95+
resp.Diagnostics.Append(diags...)
96+
if resp.Diagnostics.HasError() {
97+
return
98+
}
99+
100+
// Get the organization name from resource or provider config
101+
var orgName string
102+
resp.Diagnostics.Append(r.config.dataOrDefaultOrganization(ctx, req.Config, &orgName)...)
103+
if resp.Diagnostics.HasError() {
104+
return
105+
}
106+
107+
options := tfe.AWSOIDCConfigurationCreateOptions{
108+
RoleARN: plan.RoleARN.ValueString(),
109+
}
110+
111+
tflog.Debug(ctx, fmt.Sprintf("Create TFE AWS OIDC Configuration for organization %s", orgName))
112+
oidc, err := r.config.Client.AWSOIDCConfigurations.Create(ctx, orgName, options)
113+
if err != nil {
114+
resp.Diagnostics.AddError("Error creating TFE AWS OIDC Configuration", err.Error())
115+
return
116+
}
117+
result := modelFromTFEAWSOIDCConfiguration(oidc)
118+
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
119+
}
120+
121+
func (r *resourceTFEAWSOIDCConfiguration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
122+
// Read Terraform state into the model
123+
var state modelTFEAWSOIDCConfiguration
124+
diags := req.State.Get(ctx, &state)
125+
resp.Diagnostics.Append(diags...)
126+
if resp.Diagnostics.HasError() {
127+
return
128+
}
129+
130+
oidcID := state.ID.ValueString()
131+
tflog.Debug(ctx, fmt.Sprintf("Read AWS OIDC configuration: %s", oidcID))
132+
oidc, err := r.config.Client.AWSOIDCConfigurations.Read(ctx, oidcID)
133+
if err != nil {
134+
if errors.Is(err, tfe.ErrResourceNotFound) {
135+
tflog.Debug(ctx, fmt.Sprintf("AWS OIDC configuration %s no longer exists", oidcID))
136+
resp.State.RemoveResource(ctx)
137+
return
138+
}
139+
resp.Diagnostics.AddError(
140+
fmt.Sprintf("Error reading AWS OIDC configuration %s", oidcID),
141+
err.Error(),
142+
)
143+
return
144+
}
145+
result := modelFromTFEAWSOIDCConfiguration(oidc)
146+
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
147+
}
148+
149+
func (r *resourceTFEAWSOIDCConfiguration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
150+
var plan modelTFEAWSOIDCConfiguration
151+
diags := req.Plan.Get(ctx, &plan)
152+
resp.Diagnostics.Append(diags...)
153+
154+
var state modelTFEAWSOIDCConfiguration
155+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
156+
if resp.Diagnostics.HasError() {
157+
return
158+
}
159+
160+
options := tfe.AWSOIDCConfigurationUpdateOptions{
161+
RoleARN: plan.RoleARN.ValueString(),
162+
}
163+
164+
oidcID := state.ID.ValueString()
165+
tflog.Debug(ctx, fmt.Sprintf("Update TFE AWS OIDC Configuration %s", oidcID))
166+
oidc, err := r.config.Client.AWSOIDCConfigurations.Update(ctx, oidcID, options)
167+
if err != nil {
168+
resp.Diagnostics.AddError("Error updating TFE AWS OIDC Configuration", err.Error())
169+
return
170+
}
171+
172+
result := modelFromTFEAWSOIDCConfiguration(oidc)
173+
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
174+
}
175+
176+
func (r *resourceTFEAWSOIDCConfiguration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
177+
var state modelTFEAWSOIDCConfiguration
178+
diags := req.State.Get(ctx, &state)
179+
resp.Diagnostics.Append(diags...)
180+
if resp.Diagnostics.HasError() {
181+
return
182+
}
183+
184+
oidcID := state.ID.ValueString()
185+
tflog.Debug(ctx, fmt.Sprintf("Delete TFE AWS OIDC configuration: %s", oidcID))
186+
err := r.config.Client.AWSOIDCConfigurations.Delete(ctx, oidcID)
187+
if err != nil {
188+
if errors.Is(err, tfe.ErrResourceNotFound) {
189+
tflog.Debug(ctx, fmt.Sprintf("TFE AWS OIDC configuration %s no longer exists", oidcID))
190+
return
191+
}
192+
193+
resp.Diagnostics.AddError("Error deleting TFE AWS OIDC Configuration", err.Error())
194+
return
195+
}
196+
}
197+
198+
func modelFromTFEAWSOIDCConfiguration(p *tfe.AWSOIDCConfiguration) modelTFEAWSOIDCConfiguration {
199+
return modelTFEAWSOIDCConfiguration{
200+
ID: types.StringValue(p.ID),
201+
RoleARN: types.StringValue(p.RoleARN),
202+
Organization: types.StringValue(p.Organization.Name),
203+
}
204+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package provider
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-testing/terraform"
10+
)
11+
12+
func TestAccTFEAWSOIDCConfiguration_basic(t *testing.T) {
13+
skipUnlessHYOKEnabled(t)
14+
15+
orgName := os.Getenv("HYOK_ORGANIZATION_NAME")
16+
if orgName == "" {
17+
t.Skip("Skipping test. Set HYOK_ORGANIZATION_NAME environment to enable test.")
18+
}
19+
20+
originalRoleARN := "arn:aws:iam::123456789012:role/terraform-provider-tfe-example-1"
21+
newRoleARN := "arn:aws:iam::123456789012:role/terraform-provider-tfe-example-2"
22+
23+
resource.Test(t, resource.TestCase{
24+
PreCheck: func() { testAccPreCheck(t) },
25+
ProtoV6ProviderFactories: testAccMuxedProviders,
26+
CheckDestroy: testAccCheckTFEAWSOIDCConfigurationDestroy,
27+
Steps: []resource.TestStep{
28+
{
29+
Config: testAccTFEAWSOIDCConfigurationConfig(orgName, originalRoleARN),
30+
Check: resource.ComposeAggregateTestCheckFunc(
31+
resource.TestCheckResourceAttrSet("tfe_aws_oidc_configuration.test", "id"),
32+
resource.TestCheckResourceAttr("tfe_aws_oidc_configuration.test", "role_arn", originalRoleARN),
33+
),
34+
},
35+
// Import
36+
{
37+
ResourceName: "tfe_aws_oidc_configuration.test",
38+
ImportState: true,
39+
ImportStateVerify: true,
40+
},
41+
// Update role ARN
42+
{
43+
Config: testAccTFEAWSOIDCConfigurationConfig(orgName, newRoleARN),
44+
Check: resource.ComposeAggregateTestCheckFunc(
45+
resource.TestCheckResourceAttrSet("tfe_aws_oidc_configuration.test", "id"),
46+
resource.TestCheckResourceAttr("tfe_aws_oidc_configuration.test", "role_arn", newRoleARN),
47+
),
48+
},
49+
},
50+
})
51+
}
52+
53+
func testAccTFEAWSOIDCConfigurationConfig(orgName string, roleARN string) string {
54+
return fmt.Sprintf(`
55+
resource "tfe_aws_oidc_configuration" "test" {
56+
role_arn = "%s"
57+
organization = "%s"
58+
}
59+
`, roleARN, orgName)
60+
}
61+
62+
func testAccCheckTFEAWSOIDCConfigurationDestroy(s *terraform.State) error {
63+
for _, rs := range s.RootModule().Resources {
64+
if rs.Type != "tfe_aws_oidc_configuration" {
65+
continue
66+
}
67+
68+
if rs.Primary.ID == "" {
69+
return fmt.Errorf("no instance ID is set")
70+
}
71+
72+
_, err := testAccConfiguredClient.Client.AWSOIDCConfigurations.Read(ctx, rs.Primary.ID)
73+
if err == nil {
74+
return fmt.Errorf("TFE AWS OIDC Configuration %s still exists", rs.Primary.ID)
75+
}
76+
}
77+
78+
return nil
79+
}

0 commit comments

Comments
 (0)