diff --git a/docs/resources/group.md b/docs/resources/group.md index 5579753..2d265d9 100644 --- a/docs/resources/group.md +++ b/docs/resources/group.md @@ -5,6 +5,7 @@ subcategory: "" description: |- A group on the Coder deployment. Creating groups requires an Enterprise license. + When importing, the ID supplied can be either a group UUID retrieved via the API or /. --- # coderd_group (Resource) @@ -13,6 +14,8 @@ A group on the Coder deployment. Creating groups requires an Enterprise license. +When importing, the ID supplied can be either a group UUID retrieved via the API or `/`. + ## Example Usage ```terraform diff --git a/docs/resources/template.md b/docs/resources/template.md index 03af184..92d9fc7 100644 --- a/docs/resources/template.md +++ b/docs/resources/template.md @@ -5,6 +5,7 @@ subcategory: "" description: |- A Coder template. Logs from building template versions are streamed from the provisioner when the TF_LOG environment variable is INFO or higher. + When importing, the ID supplied can be either a template UUID retrieved via the API or /. --- # coderd_template (Resource) @@ -13,6 +14,8 @@ A Coder template. Logs from building template versions are streamed from the provisioner when the `TF_LOG` environment variable is `INFO` or higher. +When importing, the ID supplied can be either a template UUID retrieved via the API or `/`. + ## Example Usage ```terraform diff --git a/docs/resources/user.md b/docs/resources/user.md index e1c6b68..1671fa6 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -4,12 +4,15 @@ page_title: "coderd_user Resource - terraform-provider-coderd" subcategory: "" description: |- A user on the Coder deployment. + When importing, the ID supplied can be either a user UUID or a username. --- # coderd_user (Resource) A user on the Coder deployment. +When importing, the ID supplied can be either a user UUID or a username. + ## Example Usage ```terraform diff --git a/internal/provider/group_resource.go b/internal/provider/group_resource.go index 2599f9f..c7f11bd 100644 --- a/internal/provider/group_resource.go +++ b/internal/provider/group_resource.go @@ -3,6 +3,7 @@ package provider import ( "context" "fmt" + "strings" "github.com/coder/coder/v2/codersdk" "github.com/google/uuid" @@ -60,7 +61,9 @@ func (r *GroupResource) Metadata(ctx context.Context, req resource.MetadataReque func (r *GroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "A group on the Coder deployment.\n\nCreating groups requires an Enterprise license.", + MarkdownDescription: "A group on the Coder deployment.\n\n" + + "Creating groups requires an Enterprise license.\n\n" + + "When importing, the ID supplied can be either a group UUID retrieved via the API or `/`.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -324,10 +327,30 @@ func (r *GroupResource) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *GroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + var groupID uuid.UUID client := r.data.Client - groupID, err := uuid.Parse(req.ID) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse import group ID as UUID, got error: %s", err)) + idParts := strings.Split(req.ID, "/") + if len(idParts) == 1 { + var err error + groupID, err = uuid.Parse(req.ID) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse import group ID as UUID, got error: %s", err)) + return + } + } else if len(idParts) == 2 { + org, err := client.OrganizationByName(ctx, idParts[0]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get organization with name %s: %s", idParts[0], err)) + return + } + group, err := client.GroupByOrgAndName(ctx, org.ID, idParts[1]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get group with name %s: %s", idParts[1], err)) + return + } + groupID = group.ID + } else { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or `/`") return } group, err := client.Group(ctx, groupID) @@ -339,5 +362,5 @@ func (r *GroupResource) ImportState(ctx context.Context, req resource.ImportStat resp.Diagnostics.AddError("Client Error", "Cannot import groups created via OIDC") return } - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), groupID.String())...) } diff --git a/internal/provider/group_resource_test.go b/internal/provider/group_resource_test.go index fbf751e..159856f 100644 --- a/internal/provider/group_resource_test.go +++ b/internal/provider/group_resource_test.go @@ -78,14 +78,21 @@ func TestAccGroupResource(t *testing.T) { resource.TestCheckResourceAttr("coderd_group.test", "members.0", user1.ID.String()), ), }, - // Import + // Import by ID { - Config: cfg1.String(t), ResourceName: "coderd_group.test", ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"members"}, }, + // Import by org name and group name + { + ResourceName: "coderd_group.test", + ImportState: true, + ImportStateId: "default/example-group", + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"members"}, + }, // Update and Read { Config: cfg2.String(t), diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index 57c60a9..9255f2f 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "strings" "cdr.dev/slog" "github.com/coder/coder/v2/codersdk" @@ -230,7 +231,8 @@ func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRe func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "A Coder template.\n\nLogs from building template versions are streamed from the provisioner " + - "when the `TF_LOG` environment variable is `INFO` or higher.", + "when the `TF_LOG` environment variable is `INFO` or higher.\n\n" + + "When importing, the ID supplied can be either a template UUID retrieved via the API or `/`.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -771,7 +773,28 @@ func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteReques } func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + idParts := strings.Split(req.ID, "/") + if len(idParts) == 1 { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + return + } else if len(idParts) == 2 { + client := r.data.Client + org, err := client.OrganizationByName(ctx, idParts[0]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get organization with name %s: %s", idParts[0], err)) + return + } + template, err := client.TemplateByName(ctx, org.ID, idParts[1]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get template with name %s: %s", idParts[1], err)) + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), template.ID.String())...) + return + } else { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or `/`") + return + } } // ConfigValidators implements resource.ResourceWithConfigValidators. diff --git a/internal/provider/template_resource_test.go b/internal/provider/template_resource_test.go index bea6b4b..b8b7f19 100644 --- a/internal/provider/template_resource_test.go +++ b/internal/provider/template_resource_test.go @@ -145,7 +145,7 @@ func TestAccTemplateResource(t *testing.T) { }, Check: testAccCheckNumTemplateVersions(ctx, client, 3), }, - // Import + // Import by ID { Config: cfg1.String(t), ResourceName: "coderd_template.test", @@ -155,6 +155,14 @@ func TestAccTemplateResource(t *testing.T) { // We can't import ACL as we can't currently differentiate between managed and unmanaged ACL ImportStateVerifyIgnore: []string{"versions", "acl"}, }, + // Import by org name and template name + { + ResourceName: "coderd_template.test", + ImportState: true, + ImportStateVerify: true, + ImportStateId: "default/example-template", + ImportStateVerifyIgnore: []string{"versions", "acl"}, + }, // Change existing version directory & name, update template metadata. Creates a fourth version. { Config: cfg2.String(t), diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index 3eee654..4e8de49 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -55,7 +56,8 @@ func (r *UserResource) Metadata(ctx context.Context, req resource.MetadataReques func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "A user on the Coder deployment.", + MarkdownDescription: "A user on the Coder deployment.\n\n" + + "When importing, the ID supplied can be either a user UUID or a username.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -371,6 +373,18 @@ func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, r tflog.Info(ctx, "successfully deleted user") } +// Req.ID can be either a UUID or a username. func (r *UserResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + _, err := uuid.Parse(req.ID) + if err == nil { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + return + } + client := r.data.Client + user, err := client.User(ctx, req.ID) + if err != nil { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or a valid username") + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...) } diff --git a/internal/provider/user_resource_test.go b/internal/provider/user_resource_test.go index 0c3a233..a7bb470 100644 --- a/internal/provider/user_resource_test.go +++ b/internal/provider/user_resource_test.go @@ -60,7 +60,7 @@ func TestAccUserResource(t *testing.T) { resource.TestCheckResourceAttr("coderd_user.test", "suspended", "false"), ), }, - // ImportState testing + // Import by ID { ResourceName: "coderd_user.test", ImportState: true, @@ -68,6 +68,15 @@ func TestAccUserResource(t *testing.T) { // We can't pull the password from the API. ImportStateVerifyIgnore: []string{"password"}, }, + // ImportState by username + { + ResourceName: "coderd_user.test", + ImportState: true, + ImportStateVerify: true, + ImportStateId: "example", + // We can't pull the password from the API. + ImportStateVerifyIgnore: []string{"password"}, + }, // Update and Read testing { Config: cfg2.String(t), pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy