diff --git a/docs/resources/generic_openid_connect_account.md b/docs/resources/generic_oidc_account.md similarity index 68% rename from docs/resources/generic_openid_connect_account.md rename to docs/resources/generic_oidc_account.md index 3584cf82..0444e548 100644 --- a/docs/resources/generic_openid_connect_account.md +++ b/docs/resources/generic_oidc_account.md @@ -1,19 +1,19 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "octopusdeploy_generic_openid_connect_account Resource - terraform-provider-octopusdeploy" +page_title: "octopusdeploy_generic_oidc_account Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages Generic OpenID Connect accounts in Octopus Deploy. + This resource manages a Generic OIDC Account in Octopus Deploy. --- # octopusdeploy_generic_openid_connect_account (Resource) -This resource manages Generic OpenID Connect accounts in Octopus Deploy. +This resource manages a Generic OIDC Account in Octopus Deploy. ## Example Usage ```terraform -resource "octopusdeploy_generic_openid_connect_account" "example" { +resource "octopusdeploy_generic_oidc_account" "example" { name = "Generic OpenID Connect Account (OK to Delete)" execution_subject_keys = ["space", "project"] audience = "api://default" @@ -29,10 +29,10 @@ resource "octopusdeploy_generic_openid_connect_account" "example" { ### Optional -- `audience` (String) Federated credentials audience, this value is used to establish a connection between external workload identities and Microsoft Entra ID. -- `description` (String) The description of this Generic OpenID Connect account. +- `audience` (String) The audience associated with this resource. +- `description` (String) The description of this generic oidc account. - `environments` (List of String) A list of environment IDs associated with this resource. -- `execution_subject_keys` (List of String) Keys to include in a deployment or runbook. Valid options are `space`, `environment`, `project`, `tenant`, `runbook`, `account`, `type` +- `execution_subject_keys` (List of String) Keys to include in a deployment or runbook. Valid options are `space`, `environment`, `project`, `tenant`, `runbook`, `account`, `type`. - `id` (String) The unique ID for this resource. - `space_id` (String) The space ID associated with this resource. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. diff --git a/examples/resources/octopusdeploy_generic_openid_connect_account/import.sh b/examples/resources/octopusdeploy_generic_openid_connect_account/import.sh deleted file mode 100644 index 7691c5cd..00000000 --- a/examples/resources/octopusdeploy_generic_openid_connect_account/import.sh +++ /dev/null @@ -1 +0,0 @@ -terraform import [options] octopusdeploy_generic_openid_connect_account. \ No newline at end of file diff --git a/examples/resources/octopusdeploy_generic_openid_connect_account/resource.tf b/examples/resources/octopusdeploy_generic_openid_connect_account/resource.tf deleted file mode 100644 index 833ad71e..00000000 --- a/examples/resources/octopusdeploy_generic_openid_connect_account/resource.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "octopusdeploy_generic_openid_connect_account" "example" { - name = "Generic OpenID Connect Account (OK to Delete)" - execution_subject_keys = ["space", "project"] - audience = "api://default" -} \ No newline at end of file diff --git a/go.mod b/go.mod index 5b907d7a..7fccee0f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy go 1.21 require ( - github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241202024636-075a4f06c227 + github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241203052254-726a1dc47be5 github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 diff --git a/go.sum b/go.sum index 957e0955..2c12d3f5 100644 --- a/go.sum +++ b/go.sum @@ -20,10 +20,8 @@ github.com/Microsoft/hcsshim v0.12.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/ github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4VDhOQ0Ven0= github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU= -github.com/OctopusDeploy/go-octopusdeploy/v2 v2.60.0 h1:9j4IQ1UcAuaTytlBzQ7Mmoy/dLtofYfSGNiM22+sLXs= -github.com/OctopusDeploy/go-octopusdeploy/v2 v2.60.0/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw= -github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241202024636-075a4f06c227 h1:ejQL4mdWMoF7PhOQUu/G4hZHfFYbXg+XuvXtMpFpllw= -github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241202024636-075a4f06c227/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241203052254-726a1dc47be5 h1:axZMwMhboAz4bE4GmzRkT9ogE4bgjrPvNZef9rJCINE= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.62.3-0.20241203052254-726a1dc47be5/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw= github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 h1:QfbVf0bOIRMp/WHAWsuVDB7KHoWnRsGbvDuOf2ua7k4= github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4/go.mod h1:Oq9KbiRNDBB5jFmrwnrgLX0urIqR/1ptY18TzkqXm7M= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index a6e09f9e..a1c1ba54 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -46,7 +46,6 @@ func Provider() *schema.Provider { "octopusdeploy_deployment_process": resourceDeploymentProcess(), "octopusdeploy_dynamic_worker_pool": resourceDynamicWorkerPool(), "octopusdeploy_gcp_account": resourceGoogleCloudPlatformAccount(), - "octopusdeploy_generic_openid_connect_account": resourceGenericOpenIDConnectAccount(), "octopusdeploy_kubernetes_agent_deployment_target": resourceKubernetesAgentDeploymentTarget(), "octopusdeploy_kubernetes_agent_worker": resourceKubernetesAgentWorker(), "octopusdeploy_kubernetes_cluster_deployment_target": resourceKubernetesClusterDeploymentTarget(), diff --git a/octopusdeploy/resource_generic_oidc_account.go b/octopusdeploy/resource_generic_oidc_account.go deleted file mode 100644 index 52a7fdb0..00000000 --- a/octopusdeploy/resource_generic_oidc_account.go +++ /dev/null @@ -1,95 +0,0 @@ -package octopusdeploy - -import ( - "context" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "log" -) - -func resourceGenericOpenIDConnectAccount() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceGenericOpenIDConnectAccountCreate, - DeleteContext: resourceGenericOpenIDConnectAccountDelete, - Description: "This resource manages Generic OpenID Connect accounts in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceGenericOpenIDConnectAccountRead, - Schema: getGenericOpenIdConnectAccountSchema(), - UpdateContext: resourceGenericOpenIDConnectAccountUpdate, - } -} - -func resourceGenericOpenIDConnectAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - account := expandGenericOpenIDConnectAccount(d) - - log.Printf("[INFO] creating Generic OpenID Connect account: %#v", account) - - client := m.(*client.Client) - createdAccount, err := accounts.Add(client, account) - if err != nil { - return diag.FromErr(err) - } - - if err := setGenericOpenIDConnectAccount(ctx, d, createdAccount.(*accounts.GenericOIDCAccount)); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdAccount.GetID()) - - log.Printf("[INFO] Generic OpenID Connect account created (%s)", d.Id()) - return nil -} - -func resourceGenericOpenIDConnectAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] deleting Generic OpenID Connect account (%s)", d.Id()) - - client := m.(*client.Client) - if err := accounts.DeleteByID(client, d.Get("space_id").(string), d.Id()); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - - log.Printf("[INFO] Generic OpenID Connect account deleted") - return nil -} - -func resourceGenericOpenIDConnectAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] reading Generic OpenID Connect account (%s)", d.Id()) - - client := m.(*client.Client) - accountResource, err := accounts.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "Generic OpenID Connect account") - } - - genericOIDCAccount := accountResource.(*accounts.GenericOIDCAccount) - if err := setGenericOpenIDConnectAccount(ctx, d, genericOIDCAccount); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] Generic OpenID Connect account read (%s)", d.Id()) - return nil -} - -func resourceGenericOpenIDConnectAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - account := expandGenericOpenIDConnectAccount(d) - - log.Printf("[INFO] updating Generic OpenID Connect account %#v", account) - - client := m.(*client.Client) - updatedAccount, err := accounts.Update(client, account) - if err != nil { - return diag.FromErr(err) - } - - if err := setGenericOpenIDConnectAccount(ctx, d, updatedAccount.(*accounts.GenericOIDCAccount)); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] Generic OpenID Connect account updated (%s)", d.Id()) - return nil -} diff --git a/octopusdeploy/resource_generic_oidc_account_test.go b/octopusdeploy/resource_generic_oidc_account_test.go deleted file mode 100644 index 0b5d12c9..00000000 --- a/octopusdeploy/resource_generic_oidc_account_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package octopusdeploy - -import ( - "fmt" - internalTest "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/test" - "testing" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccOctopusDeployGenericOpenIDConnectAccountBasic(t *testing.T) { - internalTest.SkipCI(t, "audience is not set on initial creation") - localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - prefix := "octopusdeploy_generic_openid_connect_account." + localName - - description := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - tenantedDeploymentMode := core.TenantedDeploymentModeTenantedOrUntenanted - - executionKeys := []string{"space"} - audience := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - - newDescription := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - - resource.Test(t, resource.TestCase{ - CheckDestroy: testAccountCheckDestroy, - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: ProtoV6ProviderFactories(), - Steps: []resource.TestStep{ - { - Check: resource.ComposeTestCheckFunc( - testAccountExists(prefix), - resource.TestCheckResourceAttr(prefix, "description", description), - resource.TestCheckResourceAttr(prefix, "name", name), - resource.TestCheckResourceAttr(prefix, "tenanted_deployment_participation", string(tenantedDeploymentMode)), - resource.TestCheckResourceAttr(prefix, "execution_subject_keys.0", executionKeys[0]), - resource.TestCheckResourceAttr(prefix, "audience", audience), - ), - Config: testGenericOpenIDConnectAccountBasic(localName, name, description, tenantedDeploymentMode, executionKeys, audience), - }, - { - Check: resource.ComposeTestCheckFunc( - testAccountExists(prefix), - resource.TestCheckResourceAttr(prefix, "description", newDescription), - resource.TestCheckResourceAttr(prefix, "name", name), - resource.TestCheckResourceAttr(prefix, "tenanted_deployment_participation", string(tenantedDeploymentMode)), - resource.TestCheckResourceAttr(prefix, "execution_subject_keys.0", executionKeys[0]), - resource.TestCheckResourceAttr(prefix, "audience", audience), - ), - Config: testGenericOpenIDConnectAccountBasic(localName, name, newDescription, tenantedDeploymentMode, executionKeys, audience), - }, - }, - }) -} - -func testGenericOpenIDConnectAccountBasic(localName string, name string, description string, tenantedDeploymentParticipation core.TenantedDeploymentMode, execution_subject_keys []string, audience string) string { - return fmt.Sprintf(`resource "octopusdeploy_generic_openid_connect_account" "%s" { - description = "%s" - name = "%s" - tenanted_deployment_participation = "%s" - execution_subject_keys = %s - audience = "%s" - } - - data "octopusdeploy_accounts" "test" { - ids = [octopusdeploy_generic_openid_connect_account.%s.id] - }`, localName, description, name, tenantedDeploymentParticipation, StringArrayToTerraformArrayFormat(execution_subject_keys), audience, localName) -} diff --git a/octopusdeploy/schema_generic_oidc_account.go b/octopusdeploy/schema_generic_oidc_account.go deleted file mode 100644 index 02a6c388..00000000 --- a/octopusdeploy/schema_generic_oidc_account.go +++ /dev/null @@ -1,96 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func expandGenericOpenIDConnectAccount(d *schema.ResourceData) *accounts.GenericOIDCAccount { - name := d.Get("name").(string) - - account, _ := accounts.NewGenericOIDCAccount(name) - account.ID = d.Id() - - if v, ok := d.GetOk("description"); ok { - account.SetDescription(v.(string)) - } - - if v, ok := d.GetOk("environments"); ok { - account.EnvironmentIDs = getSliceFromTerraformTypeList(v) - } - - if v, ok := d.GetOk("name"); ok { - account.SetName(v.(string)) - } - - if v, ok := d.GetOk("space_id"); ok { - account.SetSpaceID(v.(string)) - } - - if v, ok := d.GetOk("tenanted_deployment_participation"); ok { - account.TenantedDeploymentMode = core.TenantedDeploymentMode(v.(string)) - } - - if v, ok := d.GetOk("tenant_tags"); ok { - account.TenantTags = getSliceFromTerraformTypeList(v) - } - - if v, ok := d.GetOk("tenants"); ok { - account.TenantIDs = getSliceFromTerraformTypeList(v) - } - - if v, ok := d.GetOk("audience"); ok { - account.Audience = v.(string) - } - - if v, ok := d.GetOk("execution_subject_keys"); ok { - account.DeploymentSubjectKeys = getSliceFromTerraformTypeList(v) - } - - return account -} - -func getGenericOpenIdConnectAccountSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "description": getDescriptionSchema("Generic OpenID Connect account"), - "environments": getEnvironmentsSchema(), - "id": getIDSchema(), - "name": getNameSchema(true), - "space_id": getSpaceIDSchema(), - "tenanted_deployment_participation": getTenantedDeploymentSchema(), - "tenants": getTenantsSchema(), - "tenant_tags": getTenantTagsSchema(), - "execution_subject_keys": getSubjectKeysSchema(SchemaSubjectKeysDescriptionExecution), - "audience": getOidcAudienceSchema(), - } -} - -func setGenericOpenIDConnectAccount(ctx context.Context, d *schema.ResourceData, account *accounts.GenericOIDCAccount) error { - d.Set("description", account.GetDescription()) - d.Set("id", account.GetID()) - d.Set("name", account.GetName()) - d.Set("space_id", account.GetSpaceID()) - d.Set("tenanted_deployment_participation", account.GetTenantedDeploymentMode()) - d.Set("audience", account.Audience) - - if err := d.Set("environments", account.GetEnvironmentIDs()); err != nil { - return fmt.Errorf("error setting environments: %s", err) - } - - if err := d.Set("tenants", account.GetTenantIDs()); err != nil { - return fmt.Errorf("error setting tenants: %s", err) - } - - if err := d.Set("tenant_tags", account.TenantTags); err != nil { - return fmt.Errorf("error setting tenant_tags: %s", err) - } - - if err := d.Set("execution_subject_keys", account.DeploymentSubjectKeys); err != nil { - return fmt.Errorf("error setting execution_subject_keys: %s", err) - } - - return nil -} diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index a1cdb241..fea6f806 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -126,6 +126,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewScriptModuleResource, NewUserResource, NewServiceAccountOIDCIdentity, + NewGenericOidcResource, } } diff --git a/octopusdeploy_framework/resource_generic_oidc_account.go b/octopusdeploy_framework/resource_generic_oidc_account.go new file mode 100644 index 00000000..5de260ca --- /dev/null +++ b/octopusdeploy_framework/resource_generic_oidc_account.go @@ -0,0 +1,179 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +var _ resource.Resource = &genericOidcAccountResource{} +var _ resource.ResourceWithImportState = &genericOidcAccountResource{} + +type genericOidcAccountResource struct { + *Config +} + +func NewGenericOidcResource() resource.Resource { + return &genericOidcAccountResource{} +} + +func (r *genericOidcAccountResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName("generic_oidc_account") +} + +func (r *genericOidcAccountResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schemas.GenericOidcAccountSchema{}.GetResourceSchema() +} + +func (r *genericOidcAccountResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} +func (r *genericOidcAccountResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan schemas.GenericOidcAccountResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Creating generic oidc account", map[string]interface{}{ + "name": plan.Name.ValueString(), + }) + + account := expandGenericOidcAccountResource(ctx, plan) + createdAccount, err := accounts.Add(r.Client, account) + if err != nil { + resp.Diagnostics.AddError("Error creating generic oidc account", err.Error()) + return + } + + state := flattenGenericOidcAccountResource(ctx, createdAccount.(*accounts.GenericOIDCAccount), plan) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *genericOidcAccountResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state schemas.GenericOidcAccountResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + account, err := accounts.GetByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "genericOidcAccountResource"); err != nil { + resp.Diagnostics.AddError("unable to load generic oidc account", err.Error()) + } + return + } + + newState := flattenGenericOidcAccountResource(ctx, account.(*accounts.GenericOIDCAccount), state) + resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) +} + +func (r *genericOidcAccountResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan schemas.GenericOidcAccountResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + account := expandGenericOidcAccountResource(ctx, plan) + updatedAccount, err := accounts.Update(r.Client, account) + if err != nil { + resp.Diagnostics.AddError("Error updating generic oidc account", err.Error()) + return + } + + state := flattenGenericOidcAccountResource(ctx, updatedAccount.(*accounts.GenericOIDCAccount), plan) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *genericOidcAccountResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state schemas.GenericOidcAccountResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + err := accounts.DeleteByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Error deleting generic oidc account", err.Error()) + return + } +} + +func (r *genericOidcAccountResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + accountID := req.ID + + account, err := accounts.GetByID(r.Client, r.Client.GetSpaceID(), accountID) + if err != nil { + resp.Diagnostics.AddError( + "Error reading generic oidc account", + fmt.Sprintf("Unable to read generic oidc account with ID %s: %s", accountID, err.Error()), + ) + return + } + + genericOidcAccount, ok := account.(*accounts.GenericOIDCAccount) + if !ok { + resp.Diagnostics.AddError( + "Unexpected account type", + fmt.Sprintf("Expected generic oidc account, got: %T", account), + ) + return + } + + state := schemas.GenericOidcAccountResourceModel{ + SpaceID: types.StringValue(genericOidcAccount.GetSpaceID()), + Name: types.StringValue(genericOidcAccount.GetName()), + Description: types.StringValue(genericOidcAccount.GetDescription()), + TenantedDeploymentParticipation: types.StringValue(string(genericOidcAccount.GetTenantedDeploymentMode())), + Environments: flattenStringList(genericOidcAccount.GetEnvironmentIDs(), types.ListNull(types.StringType)), + Tenants: flattenStringList(genericOidcAccount.GetTenantIDs(), types.ListNull(types.StringType)), + TenantTags: flattenStringList(genericOidcAccount.TenantTags, types.ListNull(types.StringType)), + ExecutionSubjectKeys: flattenStringList(genericOidcAccount.DeploymentSubjectKeys, types.ListNull(types.StringType)), + Audience: types.StringValue(genericOidcAccount.Audience), + } + state.ID = types.StringValue(genericOidcAccount.ID) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func expandGenericOidcAccountResource(ctx context.Context, model schemas.GenericOidcAccountResourceModel) *accounts.GenericOIDCAccount { + account, _ := accounts.NewGenericOIDCAccount(model.Name.ValueString()) + + account.SetID(model.ID.ValueString()) + account.SetDescription(model.Description.ValueString()) + account.SetSpaceID(model.SpaceID.ValueString()) + account.SetEnvironmentIDs(util.ExpandStringList(model.Environments)) + account.SetTenantedDeploymentMode(core.TenantedDeploymentMode(model.TenantedDeploymentParticipation.ValueString())) + account.SetTenantIDs(util.ExpandStringList(model.Tenants)) + account.SetTenantTags(util.ExpandStringList(model.TenantTags)) + account.DeploymentSubjectKeys = util.ExpandStringList(model.ExecutionSubjectKeys) + account.Audience = model.Audience.ValueString() + + return account +} + +func flattenGenericOidcAccountResource(ctx context.Context, account *accounts.GenericOIDCAccount, model schemas.GenericOidcAccountResourceModel) schemas.GenericOidcAccountResourceModel { + model.ID = types.StringValue(account.GetID()) + model.SpaceID = types.StringValue(account.GetSpaceID()) + model.Name = types.StringValue(account.GetName()) + model.Description = types.StringValue(account.GetDescription()) + model.TenantedDeploymentParticipation = types.StringValue(string(account.GetTenantedDeploymentMode())) + + model.Environments = util.FlattenStringList(account.GetEnvironmentIDs()) + model.Tenants = util.FlattenStringList(account.GetTenantIDs()) + model.TenantTags = util.FlattenStringList(account.TenantTags) + + model.ExecutionSubjectKeys = util.FlattenStringList(account.DeploymentSubjectKeys) + model.Audience = types.StringValue(account.Audience) + + return model +} diff --git a/octopusdeploy_framework/resource_generic_oidc_account_test.go b/octopusdeploy_framework/resource_generic_oidc_account_test.go new file mode 100644 index 00000000..a6b792b5 --- /dev/null +++ b/octopusdeploy_framework/resource_generic_oidc_account_test.go @@ -0,0 +1,62 @@ +package octopusdeploy_framework + +import ( + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "strings" + "testing" +) + +func TestAccGenericOidcAccountBasic(t *testing.T) { + localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + resourceName := "octopusdeploy_generic_oidc_account." + localName + + description := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + tenantedDeploymentParticipation := core.TenantedDeploymentModeTenantedOrUntenanted + + executionKeys := []string{"space"} + audience := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + + config := testGenericOidcAccountBasic(localName, name, description, tenantedDeploymentParticipation, executionKeys, audience) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { TestAccPreCheck(t) }, + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttrSet(resourceName, "space_id"), + resource.TestCheckResourceAttr(resourceName, "tenanted_deployment_participation", string(tenantedDeploymentParticipation)), + resource.TestCheckResourceAttr(resourceName, "execution_subject_keys.0", executionKeys[0]), + resource.TestCheckResourceAttr(resourceName, "audience", audience), + ), + ResourceName: resourceName, + }, + }, + }) +} + +func testGenericOidcAccountBasic(localName string, name string, description string, tenantedDeploymentParticipation core.TenantedDeploymentMode, execution_subject_keys []string, audience string) string { + + execKeysStr := fmt.Sprintf(`["%s"]`, strings.Join(execution_subject_keys, `", "`)) + + return fmt.Sprintf(`resource "octopusdeploy_generic_oidc_account" "%s" { + description = "%s" + name = "%s" + tenanted_deployment_participation = "%s" + execution_subject_keys = %s + audience = "%s" + } + + data "octopusdeploy_accounts" "test" { + ids = [octopusdeploy_generic_oidc_account.%s.id] + }`, localName, description, name, tenantedDeploymentParticipation, execKeysStr, audience, localName) +} diff --git a/octopusdeploy_framework/schemas/generic_oidc_account.go b/octopusdeploy_framework/schemas/generic_oidc_account.go new file mode 100644 index 00000000..a68f46f7 --- /dev/null +++ b/octopusdeploy_framework/schemas/generic_oidc_account.go @@ -0,0 +1,86 @@ +package schemas + +import ( + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type GenericOidcAccountSchema struct{} + +var _ EntitySchema = GenericOidcAccountSchema{} + +func (a GenericOidcAccountSchema) GetDatasourceSchema() datasourceSchema.Schema { + return datasourceSchema.Schema{} +} + +func (a GenericOidcAccountSchema) GetResourceSchema() resourceSchema.Schema { + return resourceSchema.Schema{ + Description: "This resource manages a Generic OIDC Account in Octopus Deploy.", + Attributes: map[string]resourceSchema.Attribute{ + "description": util.ResourceString(). + Optional(). + Computed(). + PlanModifiers(stringplanmodifier.UseStateForUnknown()). + Default(""). + Description("The description of this generic oidc account."). + Build(), + "environments": util.ResourceList(types.StringType). + Optional(). + Computed(). + Description("A list of environment IDs associated with this resource."). + Build(), + "id": GetIdResourceSchema(), + "name": util.ResourceString(). + Required(). + Description("The name of the generic oidc account."). + Build(), + "space_id": util.ResourceString(). + Optional(). + Computed(). + PlanModifiers(stringplanmodifier.UseStateForUnknown()). + Description("The space ID associated with this resource."). + Build(), + "tenanted_deployment_participation": util.ResourceString(). + Optional(). + Computed(). + PlanModifiers(stringplanmodifier.UseStateForUnknown()). + Description("The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`."). + Build(), + "tenants": util.ResourceList(types.StringType). + Optional(). + Computed(). + Description("A list of tenant IDs associated with this resource."). + Build(), + "tenant_tags": util.ResourceList(types.StringType). + Optional(). + Computed(). + Description("A list of tenant tags associated with this resource."). + Build(), + "execution_subject_keys": util.ResourceList(types.StringType). + Optional(). + Description("Keys to include in a deployment or runbook. Valid options are `space`, `environment`, `project`, `tenant`, `runbook`, `account`, `type`."). + Build(), + "audience": util.ResourceString(). + Optional(). + Description("The audience associated with this resource."). + Build(), + }, + } +} + +type GenericOidcAccountResourceModel struct { + Description types.String `tfsdk:"description"` + Environments types.List `tfsdk:"environments"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + TenantedDeploymentParticipation types.String `tfsdk:"tenanted_deployment_participation"` + Tenants types.List `tfsdk:"tenants"` + TenantTags types.List `tfsdk:"tenant_tags"` + ExecutionSubjectKeys types.List `tfsdk:"execution_subject_keys"` + Audience types.String `tfsdk:"audience"` + + ResourceModel +}