Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/gitlab_group_saml_link: Add new resource #1243

Merged
merged 10 commits into from Sep 5, 2022
46 changes: 46 additions & 0 deletions docs/resources/group_saml_link.md
@@ -0,0 +1,46 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gitlab_group_saml_link Resource - terraform-provider-gitlab"
subcategory: ""
description: |-
The gitlab_group_saml_link resource allows to manage the lifecycle of an SAML integration with a group.
Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/groups.html#saml-group-links
---

# gitlab_group_saml_link (Resource)

The `gitlab_group_saml_link` resource allows to manage the lifecycle of an SAML integration with a group.

**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#saml-group-links)

## Example Usage

```terraform
resource "gitlab_group_saml_link" "test" {
group_id = "12345"
access_level = "developer"
saml_group_name = "samlgroupname1"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `access_level` (String) Access level for members of the SAML group. Valid values are: `guest`, `reporter`, `developer`, `maintainer`, `owner`.
- `group` (String) The ID or path of the group to add the SAML Group Link to.
- `saml_group_name` (String) The name of the SAML group.

### Read-Only

- `id` (String) The ID of this resource.

## Import

Import is supported using the following syntax:

```shell
# GitLab group saml links can be imported using an id made up of `group_id:saml_group_name`, e.g.
terraform import gitlab_group_saml_link.test "12345:samlgroupname1"
```
2 changes: 2 additions & 0 deletions examples/resources/gitlab_group_saml_link/import.sh
@@ -0,0 +1,2 @@
# GitLab group saml links can be imported using an id made up of `group_id:saml_group_name`, e.g.
terraform import gitlab_group_saml_link.test "12345:samlgroupname1"
5 changes: 5 additions & 0 deletions examples/resources/gitlab_group_saml_link/resource.tf
@@ -0,0 +1,5 @@
resource "gitlab_group_saml_link" "test" {
group_id = "12345"
access_level = "developer"
saml_group_name = "samlgroupname1"
}
125 changes: 125 additions & 0 deletions internal/provider/resource_gitlab_group_saml_link.go
@@ -0,0 +1,125 @@
package provider

import (
"context"
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
gitlab "github.com/xanzy/go-gitlab"
)

var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource {
validGroupSamlLinkAccessLevelNames := []string{
"guest",
"reporter",
"developer",
"maintainer",
"owner",
}

return &schema.Resource{
Description: `The ` + "`gitlab_group_saml_link`" + ` resource allows to manage the lifecycle of an SAML integration with a group.

**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#saml-group-links)`,

CreateContext: resourceGitlabGroupSamlLinkCreate,
ReadContext: resourceGitlabGroupSamlLinkRead,
DeleteContext: resourceGitlabGroupSamlLinkDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"group": {
Description: "The ID or path of the group to add the SAML Group Link to.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"saml_group_name": {
Description: "The name of the SAML group.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"access_level": {
Description: fmt.Sprintf("Access level for members of the SAML group. Valid values are: %s.", renderValueListForDocs(validGroupSamlLinkAccessLevelNames)),
Type: schema.TypeString,
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validGroupSamlLinkAccessLevelNames, false)),
Required: true,
ForceNew: true,
},
},
}
})

func resourceGitlabGroupSamlLinkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*gitlab.Client)

group := d.Get("group").(string)
samlGroupName := d.Get("saml_group_name").(string)
accessLevel := accessLevelNameToValue[d.Get("access_level").(string)]

options := &gitlab.AddGroupSAMLLinkOptions{
SAMLGroupName: gitlab.String(samlGroupName),
AccessLevel: gitlab.AccessLevel(accessLevel),
}

log.Printf("[DEBUG] Create GitLab Group SAML Link for group %q with name %q", group, samlGroupName)
SamlLink, _, err := client.Groups.AddGroupSAMLLink(group, options, gitlab.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

d.SetId(buildTwoPartID(&group, &SamlLink.Name))
return resourceGitlabGroupSamlLinkRead(ctx, d, meta)
}

func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*gitlab.Client)
group, samlGroupName, parse_err := parseTwoPartID(d.Id())
if parse_err != nil {
return diag.FromErr(parse_err)
}

// Try to fetch all group links from GitLab
log.Printf("[DEBUG] Read GitLab Group SAML Link for group %q", group)
samlLink, _, err := client.Groups.GetGroupSAMLLink(group, samlGroupName, nil, gitlab.WithContext(ctx))
if err != nil {
if is404(err) {
log.Printf("[DEBUG] GitLab SAML Group Link %s for group ID %s not found, removing from state", samlGroupName, group)
d.SetId("")
return nil
}
return diag.FromErr(err)
}

d.Set("group", group)
d.Set("access_level", accessLevelValueToName[samlLink.AccessLevel])
d.Set("saml_group_name", samlLink.Name)

return nil
}

func resourceGitlabGroupSamlLinkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*gitlab.Client)
group, samlGroupName, parse_err := parseTwoPartID(d.Id())
if parse_err != nil {
return diag.FromErr(parse_err)
}

log.Printf("[DEBUG] Delete GitLab Group SAML Link for group %q with name %q", group, samlGroupName)
_, err := client.Groups.DeleteGroupSAMLLink(group, samlGroupName, gitlab.WithContext(ctx))
if err != nil {
if is404(err) {
log.Printf("[WARNING] %s", err)
} else {
return diag.FromErr(err)
}
}

return nil
}
80 changes: 80 additions & 0 deletions internal/provider/resource_gitlab_group_saml_link_test.go
@@ -0,0 +1,80 @@
//go:build acceptance
// +build acceptance

package provider

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccGitlabGroupSamlLink_basic(t *testing.T) {
testAccCheckEE(t)
testAccRequiresAtLeast(t, "15.3")

testGroup := testAccCreateGroups(t, 1)[0]

resource.ParallelTest(t, resource.TestCase{
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckGitlabGroupSamlLinkDestroy,
Steps: []resource.TestStep{

// Create a group SAML link as a developer
{
Config: fmt.Sprintf(`
resource "gitlab_group_saml_link" "this" {
group = "%d"
access_level = "developer"
saml_group_name = "test_saml_group"

}
`, testGroup.ID),
},
// Verify Import
{
ResourceName: "gitlab_group_saml_link.this",
ImportState: true,
ImportStateVerify: true,
},
// Update the group SAML link to change the access level
{
Config: fmt.Sprintf(`
resource "gitlab_group_saml_link" "this" {
group = "%d"
access_level = "maintainer"
saml_group_name = "test_saml_group"

}
`, testGroup.ID),
},
},
})
}

func testAccCheckGitlabGroupSamlLinkDestroy(s *terraform.State) error {
for _, resourceState := range s.RootModule().Resources {
if resourceState.Type != "gitlab_group_saml_link" {
continue
}

group, samlGroupName, err := parseTwoPartID(resourceState.Primary.ID)
if err != nil {
return err
}

samlGroupLink, _, err := testGitlabClient.Groups.GetGroupSAMLLink(group, samlGroupName)
if err == nil {
if samlGroupLink != nil {
return fmt.Errorf("SAML Group Link still exists")
}
}
if !is404(err) {
return err
}
return nil
}
return nil
}