diff --git a/docs/data-sources/local_group_members.md b/docs/data-sources/local_group_members.md new file mode 100644 index 0000000..d615e97 --- /dev/null +++ b/docs/data-sources/local_group_members.md @@ -0,0 +1,38 @@ +--- +page_title: "windows_local_group_members Data Source - terraform-provider-windows" +subcategory: "Local" +description: |- + Retrieve a list of members for a specific local security group +--- +# windows_local_group_members (Data Source) + + +Retrieve a list of members for a specific local security group. + +## Example Usage + +```terraform +data "windows_local_group_members" "administrators" { + name = "Administrators" +} +``` + + +## Schema + +### Required + +- `name` (String) The name of the local group. + +### Read-Only + +- `members` (Attributes List) (see [below for nested schema](#nestedatt--members)) + + +### Nested Schema for `members` + +Read-Only: + +- `name` (String) The name of the local group member. +- `object_class` (String) The ObjectClass of the local group member. +- `sid` (String) The security ID of the local group member. diff --git a/examples/data-sources/windows_local_group_members/data-source.tf b/examples/data-sources/windows_local_group_members/data-source.tf new file mode 100644 index 0000000..3960ff7 --- /dev/null +++ b/examples/data-sources/windows_local_group_members/data-source.tf @@ -0,0 +1,3 @@ +data "windows_local_group_members" "administrators" { + name = "Administrators" +} diff --git a/internal/generate/datasource_local_group_members/local_group_members_data_source_gen.go b/internal/generate/datasource_local_group_members/local_group_members_data_source_gen.go new file mode 100644 index 0000000..5f6b956 --- /dev/null +++ b/internal/generate/datasource_local_group_members/local_group_members_data_source_gen.go @@ -0,0 +1,484 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package datasource_local_group_members + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func LocalGroupMembersDataSourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "members": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Computed: true, + Description: "The name of the local group member.", + MarkdownDescription: "The name of the local group member.", + }, + "object_class": schema.StringAttribute{ + Computed: true, + Description: "The ObjectClass of the local group member.", + MarkdownDescription: "The ObjectClass of the local group member.", + }, + "sid": schema.StringAttribute{ + Computed: true, + Description: "The security ID of the local group member.", + MarkdownDescription: "The security ID of the local group member.", + }, + }, + CustomType: MembersType{ + ObjectType: types.ObjectType{ + AttrTypes: MembersValue{}.AttributeTypes(ctx), + }, + }, + }, + Computed: true, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "The name of the local group.", + MarkdownDescription: "The name of the local group.", + }, + }, + } +} + +type LocalGroupMembersModel struct { + Members types.List `tfsdk:"members"` + Name types.String `tfsdk:"name"` +} + +var _ basetypes.ObjectTypable = MembersType{} + +type MembersType struct { + basetypes.ObjectType +} + +func (t MembersType) Equal(o attr.Type) bool { + other, ok := o.(MembersType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t MembersType) String() string { + return "MembersType" +} + +func (t MembersType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + nameAttribute, ok := attributes["name"] + + if !ok { + diags.AddError( + "Attribute Missing", + `name is missing from object`) + + return nil, diags + } + + nameVal, ok := nameAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`name expected to be basetypes.StringValue, was: %T`, nameAttribute)) + } + + objectClassAttribute, ok := attributes["object_class"] + + if !ok { + diags.AddError( + "Attribute Missing", + `object_class is missing from object`) + + return nil, diags + } + + objectClassVal, ok := objectClassAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`object_class expected to be basetypes.StringValue, was: %T`, objectClassAttribute)) + } + + sidAttribute, ok := attributes["sid"] + + if !ok { + diags.AddError( + "Attribute Missing", + `sid is missing from object`) + + return nil, diags + } + + sidVal, ok := sidAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`sid expected to be basetypes.StringValue, was: %T`, sidAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return MembersValue{ + Name: nameVal, + ObjectClass: objectClassVal, + Sid: sidVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewMembersValueNull() MembersValue { + return MembersValue{ + state: attr.ValueStateNull, + } +} + +func NewMembersValueUnknown() MembersValue { + return MembersValue{ + state: attr.ValueStateUnknown, + } +} + +func NewMembersValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (MembersValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing MembersValue Attribute Value", + "While creating a MembersValue value, a missing attribute value was detected. "+ + "A MembersValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("MembersValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid MembersValue Attribute Type", + "While creating a MembersValue value, an invalid attribute value was detected. "+ + "A MembersValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("MembersValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("MembersValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra MembersValue Attribute Value", + "While creating a MembersValue value, an extra attribute value was detected. "+ + "A MembersValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra MembersValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewMembersValueUnknown(), diags + } + + nameAttribute, ok := attributes["name"] + + if !ok { + diags.AddError( + "Attribute Missing", + `name is missing from object`) + + return NewMembersValueUnknown(), diags + } + + nameVal, ok := nameAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`name expected to be basetypes.StringValue, was: %T`, nameAttribute)) + } + + objectClassAttribute, ok := attributes["object_class"] + + if !ok { + diags.AddError( + "Attribute Missing", + `object_class is missing from object`) + + return NewMembersValueUnknown(), diags + } + + objectClassVal, ok := objectClassAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`object_class expected to be basetypes.StringValue, was: %T`, objectClassAttribute)) + } + + sidAttribute, ok := attributes["sid"] + + if !ok { + diags.AddError( + "Attribute Missing", + `sid is missing from object`) + + return NewMembersValueUnknown(), diags + } + + sidVal, ok := sidAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`sid expected to be basetypes.StringValue, was: %T`, sidAttribute)) + } + + if diags.HasError() { + return NewMembersValueUnknown(), diags + } + + return MembersValue{ + Name: nameVal, + ObjectClass: objectClassVal, + Sid: sidVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewMembersValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) MembersValue { + object, diags := NewMembersValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewMembersValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t MembersType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewMembersValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewMembersValueUnknown(), nil + } + + if in.IsNull() { + return NewMembersValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewMembersValueMust(MembersValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t MembersType) ValueType(ctx context.Context) attr.Value { + return MembersValue{} +} + +var _ basetypes.ObjectValuable = MembersValue{} + +type MembersValue struct { + Name basetypes.StringValue `tfsdk:"name"` + ObjectClass basetypes.StringValue `tfsdk:"object_class"` + Sid basetypes.StringValue `tfsdk:"sid"` + state attr.ValueState +} + +func (v MembersValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 3) + + var val tftypes.Value + var err error + + attrTypes["name"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["object_class"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["sid"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 3) + + val, err = v.Name.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["name"] = val + + val, err = v.ObjectClass.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["object_class"] = val + + val, err = v.Sid.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["sid"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v MembersValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v MembersValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v MembersValue) String() string { + return "MembersValue" +} + +func (v MembersValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + objVal, diags := types.ObjectValue( + map[string]attr.Type{ + "name": basetypes.StringType{}, + "object_class": basetypes.StringType{}, + "sid": basetypes.StringType{}, + }, + map[string]attr.Value{ + "name": v.Name, + "object_class": v.ObjectClass, + "sid": v.Sid, + }) + + return objVal, diags +} + +func (v MembersValue) Equal(o attr.Value) bool { + other, ok := o.(MembersValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.Name.Equal(other.Name) { + return false + } + + if !v.ObjectClass.Equal(other.ObjectClass) { + return false + } + + if !v.Sid.Equal(other.Sid) { + return false + } + + return true +} + +func (v MembersValue) Type(ctx context.Context) attr.Type { + return MembersType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v MembersValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "name": basetypes.StringType{}, + "object_class": basetypes.StringType{}, + "sid": basetypes.StringType{}, + } +} diff --git a/internal/provider/local/local_group_members_data_source.go b/internal/provider/local/local_group_members_data_source.go new file mode 100644 index 0000000..82e3b04 --- /dev/null +++ b/internal/provider/local/local_group_members_data_source.go @@ -0,0 +1,88 @@ +package local + +import ( + "context" + "fmt" + "terraform-provider-windows/internal/generate/datasource_local_group_members" + + "github.com/d-strobel/gowindows" + "github.com/d-strobel/gowindows/windows/local/accounts" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = (*localGroupMembersDataSource)(nil) + +func NewLocalGroupMembersDataSource() datasource.DataSource { + return &localGroupMembersDataSource{} +} + +type localGroupMembersDataSource struct { + client *gowindows.Client +} + +func (d *localGroupMembersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_local_group_members" +} + +func (d *localGroupMembersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = datasource_local_group_members.LocalGroupMembersDataSourceSchema(ctx) + resp.Schema.Description = `Retrieve a list of members for a specific local security group.` +} + +func (d *localGroupMembersDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*gowindows.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *gowindows.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + d.client = client +} + +func (d *localGroupMembersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data datasource_local_group_members.LocalGroupMembersModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + params := accounts.GroupMemberListParams{ + Name: data.Name.ValueString(), + } + winResp, err := d.client.LocalAccounts.GroupMemberList(ctx, params) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read local group members, got error: %s", err)) + return + } + + var membersValueList []datasource_local_group_members.MembersValue + + for _, member := range winResp { + memberValue := datasource_local_group_members.MembersValue{ + Name: types.StringValue(member.Name), + Sid: types.StringValue(member.SID.Value), + ObjectClass: types.StringValue(member.ObjectClass), + } + objVal, _ := memberValue.ToObjectValue(ctx) + newMembersValue, _ := datasource_local_group_members.NewMembersValue(objVal.AttributeTypes(ctx), objVal.Attributes()) + + membersValueList = append(membersValueList, newMembersValue) + } + + data.Members, _ = types.ListValueFrom(ctx, datasource_local_group_members.MembersValue{}.Type(ctx), membersValueList) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index bccf7fa..469ed1c 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -55,6 +55,7 @@ func (p *WindowsProvider) DataSources(ctx context.Context) []func() datasource.D local.NewLocalGroupsDataSource, local.NewLocalUserDataSource, local.NewLocalUsersDataSource, + local.NewLocalGroupMembersDataSource, } } diff --git a/internal/schema/local_datasources.json b/internal/schema/local_datasources.json index c320ec0..0b7abb5 100644 --- a/internal/schema/local_datasources.json +++ b/internal/schema/local_datasources.json @@ -1,364 +1,409 @@ { - "version": "0.1", - "provider": { - "name": "windows" + "version": "0.1", + "provider": { + "name": "windows" + }, + "datasources": [ + { + "name": "local_group", + "schema": { + "attributes": [ + { + "name": "description", + "string": { + "computed_optional_required": "computed", + "description": "The description of the local security group." + } + }, + { + "name": "id", + "string": { + "computed_optional_required": "computed", + "description": "The ID of the retrieved local security group. This is the same as the SID." + } + }, + { + "name": "name", + "string": { + "computed_optional_required": "optional", + "description": "Define the name of the local security group." + } + }, + { + "name": "sid", + "string": { + "computed_optional_required": "optional", + "description": "Define the security ID of the local security group." + } + } + ] + } }, - "datasources": [ - { - "name": "local_group", - "schema": { + { + "name": "local_groups", + "schema": { + "attributes": [ + { + "name": "groups", + "list_nested": { + "computed_optional_required": "computed", + "nested_object": { "attributes": [ - { - "name": "description", - "string": { - "computed_optional_required": "computed", - "description": "The description of the local security group." - } - }, - { - "name": "id", - "string": { - "computed_optional_required": "computed", - "description": "The ID of the retrieved local security group. This is the same as the SID." - } - }, - { - "name": "name", - "string": { - "computed_optional_required": "optional", - "description": "Define the name of the local security group." - } - }, - { - "name": "sid", - "string": { - "computed_optional_required": "optional", - "description": "Define the security ID of the local security group." - } + { + "name": "description", + "string": { + "computed_optional_required": "computed", + "description": "The description of the local security group." } - ] - } - }, - { - "name": "local_groups", - "schema": { - "attributes": [ - { - "name": "groups", - "list_nested": { - "computed_optional_required": "computed", - "nested_object": { - "attributes": [ - { - "name": "description", - "string": { - "computed_optional_required": "computed", - "description": "The description of the local security group." - } - }, - { - "name": "id", - "string": { - "computed_optional_required": "computed", - "description": "The ID of the retrieved local security group. This is the same as the SID." - } - }, - { - "name": "name", - "string": { - "computed_optional_required": "computed", - "description": "The name of the local security group." - } - }, - { - "name": "sid", - "string": { - "computed_optional_required": "computed", - "description": "The security ID of the local security group." - } - } - ] - } - } + }, + { + "name": "id", + "string": { + "computed_optional_required": "computed", + "description": "The ID of the retrieved local security group. This is the same as the SID." + } + }, + { + "name": "name", + "string": { + "computed_optional_required": "computed", + "description": "The name of the local security group." } + }, + { + "name": "sid", + "string": { + "computed_optional_required": "computed", + "description": "The security ID of the local security group." + } + } ] + } + } + } + ] + } + }, + { + "name": "local_user", + "schema": { + "attributes": [ + { + "name": "account_expires", + "string": { + "computed_optional_required": "computed", + "description": "Retrieve the time where the local user account expires.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "description", + "string": { + "computed_optional_required": "computed", + "description": "The description of the local user." + } + }, + { + "name": "enabled", + "bool": { + "computed_optional_required": "computed", + "description": "Get the status of the local user." + } + }, + { + "name": "full_name", + "string": { + "computed_optional_required": "computed", + "description": "The full name of the local user." + } + }, + { + "name": "id", + "string": { + "computed_optional_required": "computed", + "description": "The ID of the retrieved local user. This is the same as the SID." + } + }, + { + "name": "last_logon", + "string": { + "computed_optional_required": "computed", + "description": "The last logon time of the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "name", + "string": { + "computed_optional_required": "required", + "description": "Define the name of the local user." + } + }, + { + "name": "password_changeable_date", + "string": { + "computed_optional_required": "computed", + "description": "The password changeable date of the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "password_expires", + "string": { + "computed_optional_required": "computed", + "description": "The time when the password of the local user expires.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "password_last_set", + "string": { + "computed_optional_required": "computed", + "description": "The last time when the password was set for the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } } - }, - { - "name": "local_user", - "schema": { + }, + { + "name": "password_required", + "bool": { + "computed_optional_required": "computed", + "description": "If true a password is required login with the local user." + } + }, + { + "name": "sid", + "string": { + "computed_optional_required": "optional", + "description": "The security ID of the local user." + } + }, + { + "name": "user_may_change_password", + "bool": { + "computed_optional_required": "computed", + "description": "If true the local user can change it's password." + } + } + ] + } + }, + { + "name": "local_users", + "schema": { + "attributes": [ + { + "name": "users", + "list_nested": { + "computed_optional_required": "computed", + "nested_object": { "attributes": [ - { - "name": "account_expires", - "string": { - "computed_optional_required": "computed", - "description": "Retrieve the time where the local user account expires.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "description", - "string": { - "computed_optional_required": "computed", - "description": "The description of the local user." - } - }, - { - "name": "enabled", - "bool": { - "computed_optional_required": "computed", - "description": "Get the status of the local user." - } - }, - { - "name": "full_name", - "string": { - "computed_optional_required": "computed", - "description": "The full name of the local user." - } - }, - { - "name": "id", - "string": { - "computed_optional_required": "computed", - "description": "The ID of the retrieved local user. This is the same as the SID." - } - }, - { - "name": "last_logon", - "string": { - "computed_optional_required": "computed", - "description": "The last logon time of the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "name", - "string": { - "computed_optional_required": "required", - "description": "Define the name of the local user." - } - }, - { - "name": "password_changeable_date", - "string": { - "computed_optional_required": "computed", - "description": "The password changeable date of the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_expires", - "string": { - "computed_optional_required": "computed", - "description": "The time when the password of the local user expires.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_last_set", - "string": { - "computed_optional_required": "computed", - "description": "The last time when the password was set for the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_required", - "bool": { - "computed_optional_required": "computed", - "description": "If true a password is required login with the local user." - } - }, - { - "name": "sid", - "string": { - "computed_optional_required": "optional", - "description": "The security ID of the local user." - } - }, - { - "name": "user_may_change_password", - "bool": { - "computed_optional_required": "computed", - "description": "If true the local user can change it's password." - } + { + "name": "account_expires", + "string": { + "computed_optional_required": "computed", + "description": "Retrieve the time where the local user account expires.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "description", + "string": { + "computed_optional_required": "computed", + "description": "The description of the local user." + } + }, + { + "name": "enabled", + "bool": { + "computed_optional_required": "computed", + "description": "Get the status of the local user." + } + }, + { + "name": "full_name", + "string": { + "computed_optional_required": "computed", + "description": "The full name of the local user." + } + }, + { + "name": "id", + "string": { + "computed_optional_required": "computed", + "description": "The ID of the retrieved local user. This is the same as the SID." + } + }, + { + "name": "last_logon", + "string": { + "computed_optional_required": "computed", + "description": "The last logon time of the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "name", + "string": { + "computed_optional_required": "required", + "description": "Define the name of the local user." } + }, + { + "name": "password_changeable_date", + "string": { + "computed_optional_required": "computed", + "description": "The password changeable date of the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "password_expires", + "string": { + "computed_optional_required": "computed", + "description": "The time when the password of the local user expires.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "password_last_set", + "string": { + "computed_optional_required": "computed", + "description": "The last time when the password was set for the local user.", + "custom_type": { + "import": { + "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + }, + "type": "timetypes.RFC3339Type{}", + "value_type": "timetypes.RFC3339" + } + } + }, + { + "name": "password_required", + "bool": { + "computed_optional_required": "computed", + "description": "If true a password is required login with the local user." + } + }, + { + "name": "sid", + "string": { + "computed_optional_required": "optional", + "description": "The security ID of the local user." + } + }, + { + "name": "user_may_change_password", + "bool": { + "computed_optional_required": "computed", + "description": "If true the local user can change it's password." + } + } ] + } + } + } + ] + } + }, + { + "name": "local_group_members", + "schema": { + "attributes": [ + { + "name": "name", + "string": { + "computed_optional_required": "required", + "description": "The name of the local group." } - }, - { - "name": "local_users", - "schema": { + }, + { + "name": "members", + "list_nested": { + "computed_optional_required": "computed", + "nested_object": { "attributes": [ - { - "name": "users", - "list_nested": { - "computed_optional_required": "computed", - "nested_object": { - "attributes": [ - { - "name": "account_expires", - "string": { - "computed_optional_required": "computed", - "description": "Retrieve the time where the local user account expires.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "description", - "string": { - "computed_optional_required": "computed", - "description": "The description of the local user." - } - }, - { - "name": "enabled", - "bool": { - "computed_optional_required": "computed", - "description": "Get the status of the local user." - } - }, - { - "name": "full_name", - "string": { - "computed_optional_required": "computed", - "description": "The full name of the local user." - } - }, - { - "name": "id", - "string": { - "computed_optional_required": "computed", - "description": "The ID of the retrieved local user. This is the same as the SID." - } - }, - { - "name": "last_logon", - "string": { - "computed_optional_required": "computed", - "description": "The last logon time of the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "name", - "string": { - "computed_optional_required": "required", - "description": "Define the name of the local user." - } - }, - { - "name": "password_changeable_date", - "string": { - "computed_optional_required": "computed", - "description": "The password changeable date of the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_expires", - "string": { - "computed_optional_required": "computed", - "description": "The time when the password of the local user expires.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_last_set", - "string": { - "computed_optional_required": "computed", - "description": "The last time when the password was set for the local user.", - "custom_type": { - "import": { - "path": "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - }, - "type": "timetypes.RFC3339Type{}", - "value_type": "timetypes.RFC3339" - } - } - }, - { - "name": "password_required", - "bool": { - "computed_optional_required": "computed", - "description": "If true a password is required login with the local user." - } - }, - { - "name": "sid", - "string": { - "computed_optional_required": "optional", - "description": "The security ID of the local user." - } - }, - { - "name": "user_may_change_password", - "bool": { - "computed_optional_required": "computed", - "description": "If true the local user can change it's password." - } - } - ] - } - } + { + "name": "name", + "string": { + "computed_optional_required": "computed", + "description": "The name of the local group member." + } + }, + { + "name": "sid", + "string": { + "computed_optional_required": "computed", + "description": "The security ID of the local group member." + } + }, + { + "name": "object_class", + "string": { + "computed_optional_required": "computed", + "description": "The ObjectClass of the local group member." } + } ] + } } - } - ] + } + ] + } + } + ] }