Skip to content

Commit

Permalink
Add resource and datasource to manage OrganizationalUnit
Browse files Browse the repository at this point in the history
  • Loading branch information
pablo-ruth committed Jan 23, 2023
1 parent 21e8c27 commit b7c3ad0
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 3 deletions.
25 changes: 25 additions & 0 deletions docs/data-sources/ou.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ldap_ou

`ldap_ou` is a data source for managing an LDAP OU.

## Example Usage

```hcl
data "ldap_ou" "ou" {
ou = "OU=MyCompany,DC=domain,DC=tld"
name = "MyOU"
scope = 2
}
```

## Argument Reference

* `name` - (Required) LDAP OU name.
* `ou` - (Required) OU where LDAP OU will be search.
* `scope` - (Optional) LDAP search scope (0: BaseObject, 1: SingleLevel, 2: WholeSubtree) Defaults to `0`.

## Attribute Reference

* `id` - The DN of the LDAP OU.
* `description` - Description attribute for the LDAP OU
* `managed_by` - ManagedBy attribute.
32 changes: 32 additions & 0 deletions docs/resources/ou.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ldap_ou

`ldap_ou` is a resource for managing an LDAP OU.

## Example Usage

```hcl
resource "ldap_ou" "ou" {
name = "MyOU"
ou = "OU=MyCompany,DC=domain,DC=tld"
description = "My OU description"
}
```

## Argument Reference

* `ou` - (Required) OU where LDAP OU will be created.
* `name` - (Required) LDAP OU name.
* `description` - (Optional) Description attribute for the LDAP OU. Defaults to empty.
* `managed_by` - (Optional) ManagedBy attribute. Defaults to ``.

## Attribute Reference

* `id` - The DN of the LDAP OU.

## Import

LDAP OU can be imported using the full LDAP DN (id), e.g.

```
$ terraform import ldap_ou.example OU=Myou,OU=MyCompany,DC=domain,DC=tld
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/Ouest-France/terraform-provider-ldap
go 1.18

require (
github.com/Ouest-France/goldap v0.6.2
github.com/Ouest-France/goldap v0.6.4
github.com/go-ldap/ldap/v3 v3.4.4
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Ouest-France/goldap v0.6.2 h1:8uTl/RSbTf/yjHRyhrSRHARSXgaqXqGZmTnB5EODAa8=
github.com/Ouest-France/goldap v0.6.2/go.mod h1:1oPqn1er8HgJglFFCRpY+puY6mnSJQYVxb77wMuCxAA=
github.com/Ouest-France/goldap v0.6.4 h1:YZDVku6NpYm5UTtue0iWsucMNHNzx7BLSoIm81CtoFE=
github.com/Ouest-France/goldap v0.6.4/go.mod h1:1oPqn1er8HgJglFFCRpY+puY6mnSJQYVxb77wMuCxAA=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
Expand Down
74 changes: 74 additions & 0 deletions ldap/data_source_resource_ldap_ou.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ldap

import (
"context"
"fmt"

"github.com/Ouest-France/goldap"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceLDAPOU() *schema.Resource {
return &schema.Resource{
Description: "`ldap_ou` is a data source for getting an LDAP OU.",
ReadContext: dataSourceLDAPOURead,

Schema: map[string]*schema.Schema{
"id": {
Description: "The DN of the LDAP OU.",
Type: schema.TypeString,
Computed: true,
},
"ou": {
Description: "OU where LDAP OU will be search.",
Type: schema.TypeString,
Required: true,
},
"name": {
Description: "LDAP OU name.",
Type: schema.TypeString,
Required: true,
},
"scope": {
Description: "LDAP search scope",
Type: schema.TypeInt,
Optional: true,
Default: 0,
},
"description": {
Description: "Description attribute for the LDAP OU",
Type: schema.TypeString,
Computed: true,
},
"managed_by": {
Description: "ManagedBy attribute",
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceLDAPOURead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {

// Get scope
scope := d.Get("scope").(int)
if scope < 0 || scope > 2 {
return diag.FromErr(fmt.Errorf("scope must be between 0 and 2, got %d", scope))
}

// If scope is 1 or 2, we search the OU DN given the OU name and the OU
client := m.(*goldap.Client)

// Search OU
dn, err := client.SearchOUByName(d.Get("name").(string), d.Get("ou").(string), scope)
if err != nil {
return diag.FromErr(err)
}

d.SetId(dn)

// Add context key to signal the Read is called from a datasource
return resourceLDAPOURead(context.WithValue(ctx, CallerTypeKey, DatasourceCaller), d, m)
}
2 changes: 2 additions & 0 deletions ldap/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ func Provider() *schema.Provider {
},
ResourcesMap: map[string]*schema.Resource{
"ldap_group": resourceLDAPGroup(),
"ldap_ou": resourceLDAPOU(),
},
DataSourcesMap: map[string]*schema.Resource{
"ldap_group": dataSourceLDAPGroup(),
"ldap_user": dataSourceLDAPUser(),
"ldap_ou": dataSourceLDAPOU(),
},
ConfigureFunc: providerConfigure,
}
Expand Down
156 changes: 156 additions & 0 deletions ldap/resource_ldap_ou.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package ldap

import (
"context"
"fmt"
"strings"

"github.com/Ouest-France/goldap"
"github.com/go-ldap/ldap/v3"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceLDAPOU() *schema.Resource {
return &schema.Resource{
Description: "`ldap_ou` is a resource for managing an LDAP OU.",
CreateContext: resourceLDAPOUCreate,
ReadContext: resourceLDAPOURead,
UpdateContext: resourceLDAPOUUpdate,
DeleteContext: resourceLDAPOUDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"id": {
Description: "The DN of the LDAP OU.",
Type: schema.TypeString,
Computed: true,
},
"ou": {
Description: "OU where LDAP OU will be created.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Description: "LDAP OU name.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": {
Description: "Description attribute for the LDAP OU.",
Type: schema.TypeString,
Optional: true,
},
"managed_by": {
Description: "ManagedBy attribute",
Type: schema.TypeString,
Optional: true,
Default: "",
},
},
}
}

func resourceLDAPOUCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*goldap.Client)

dn := fmt.Sprintf("OU=%s,%s", d.Get("name").(string), d.Get("ou").(string))

err := client.CreateOrganizationalUnit(dn, d.Get("description").(string), d.Get("managed_by").(string))
if err != nil {
return diag.FromErr(err)
}

d.SetId(dn)

return resourceLDAPOURead(ctx, d, m)
}

func resourceLDAPOURead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*goldap.Client)

dn := d.Id()

attributes, err := client.ReadOrganizationalUnit(dn)
if err != nil {
if err.(*ldap.Error).ResultCode == 32 {
// Object doesn't exist

// If Read is called from a datasource, return an error
if ctx.Value(CallerTypeKey) == DatasourceCaller {
return diag.Errorf("LDAP OU not found: %s", dn)
}

// If not a call from datasource, remove the resource from the state
// and cleanly return
d.SetId("")
return nil
}
return diag.FromErr(err)
}

nameAttr, ok := attributes["ou"]
if !ok || len(nameAttr) != 1 {
return diag.Errorf("LDAP attribute \"name\" doesn't exist or is empty for OU: %s", dn)
}

if err := d.Set("name", nameAttr[0]); err != nil {
return diag.FromErr(err)
}

// Remove the `OU=<ou-name>,` from the DN to get the OU
ou := strings.ReplaceAll(dn, fmt.Sprintf("OU=%s,", attributes["name"][0]), "")
if err := d.Set("ou", ou); err != nil {
return diag.FromErr(err)
}

desc := ""
if val, ok := attributes["description"]; ok {
desc = val[0]
}
if err := d.Set("description", desc); err != nil {
return diag.FromErr(err)
}

managedBy := ""
if val, ok := attributes["managedBy"]; ok {
managedBy = val[0]
}
if err := d.Set("managed_by", managedBy); err != nil {
return diag.FromErr(err)
}

return diag.FromErr(err)
}

func resourceLDAPOUUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*goldap.Client)
dn := fmt.Sprintf("OU=%s,%s", d.Get("name").(string), d.Get("ou").(string))

if d.HasChange("description") {
if err := client.UpdateOrganizationalUnitDescription(dn, d.Get("description").(string)); err != nil {
return diag.FromErr(err)
}
}

if d.HasChange("managed_by") {
if err := client.UpdateOrganizationalUnitManagedBy(dn, d.Get("managed_by").(string)); err != nil {
return diag.FromErr(err)
}
}

return resourceLDAPOURead(ctx, d, m)
}

func resourceLDAPOUDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*goldap.Client)
dn := fmt.Sprintf("OU=%s,%s", d.Get("name").(string), d.Get("ou").(string))

err := client.DeleteOrganizationalUnit(dn)

return diag.FromErr(err)
}

0 comments on commit b7c3ad0

Please sign in to comment.