diff --git a/docs/data-sources/group_hook.md b/docs/data-sources/group_hook.md
new file mode 100644
index 000000000..16b72c810
--- /dev/null
+++ b/docs/data-sources/group_hook.md
@@ -0,0 +1,59 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "gitlab_group_hook Data Source - terraform-provider-gitlab"
+subcategory: ""
+description: |-
+ The gitlab_group_hook data source allows to retrieve details about a hook in a group.
+ Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/groups.html#get-group-hook
+---
+
+# gitlab_group_hook (Data Source)
+
+The `gitlab_group_hook` data source allows to retrieve details about a hook in a group.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#get-group-hook)
+
+## Example Usage
+
+```terraform
+data "gitlab_group" "example" {
+ id = "foo/bar/baz"
+}
+
+data "gitlab_group_hook" "example" {
+ group = data.gitlab_group.example.id
+ hook_id = 1
+}
+```
+
+
+## Schema
+
+### Required
+
+- `group` (String) The ID or full path of the group.
+- `hook_id` (Number) The id of the group hook.
+
+### Read-Only
+
+- `confidential_issues_events` (Boolean) Invoke the hook for confidential issues events.
+- `confidential_note_events` (Boolean) Invoke the hook for confidential notes events.
+- `deployment_events` (Boolean) Invoke the hook for deployment events.
+- `enable_ssl_verification` (Boolean) Enable ssl verification when invoking the hook.
+- `group_id` (Number) The id of the group for the hook.
+- `id` (String) The ID of this resource.
+- `issues_events` (Boolean) Invoke the hook for issues events.
+- `job_events` (Boolean) Invoke the hook for job events.
+- `merge_requests_events` (Boolean) Invoke the hook for merge requests.
+- `note_events` (Boolean) Invoke the hook for notes events.
+- `pipeline_events` (Boolean) Invoke the hook for pipeline events.
+- `push_events` (Boolean) Invoke the hook for push events.
+- `push_events_branch_filter` (String) Invoke the hook for push events on matching branches only.
+- `releases_events` (Boolean) Invoke the hook for releases events.
+- `subgroup_events` (Boolean) Invoke the hook for subgroup events.
+- `tag_push_events` (Boolean) Invoke the hook for tag push events.
+- `token` (String) A token to present when invoking the hook. The token is not available for imported resources.
+- `url` (String) The url of the hook to invoke.
+- `wiki_page_events` (Boolean) Invoke the hook for wiki page events.
+
+
diff --git a/docs/data-sources/group_hooks.md b/docs/data-sources/group_hooks.md
new file mode 100644
index 000000000..b79b11693
--- /dev/null
+++ b/docs/data-sources/group_hooks.md
@@ -0,0 +1,66 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "gitlab_group_hooks Data Source - terraform-provider-gitlab"
+subcategory: ""
+description: |-
+ The gitlab_group_hooks data source allows to retrieve details about hooks in a group.
+ Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/groups.html#list-group-hooks
+---
+
+# gitlab_group_hooks (Data Source)
+
+The `gitlab_group_hooks` data source allows to retrieve details about hooks in a group.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#list-group-hooks)
+
+## Example Usage
+
+```terraform
+data "gitlab_group" "example" {
+ id = "foo/bar/baz"
+}
+
+data "gitlab_group_hooks" "examples" {
+ group = data.gitlab_group.example.id
+}
+```
+
+
+## Schema
+
+### Required
+
+- `group` (String) The ID or full path of the group.
+
+### Read-Only
+
+- `hooks` (List of Object) The list of hooks. (see [below for nested schema](#nestedatt--hooks))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `hooks`
+
+Read-Only:
+
+- `confidential_issues_events` (Boolean)
+- `confidential_note_events` (Boolean)
+- `deployment_events` (Boolean)
+- `enable_ssl_verification` (Boolean)
+- `group` (String)
+- `group_id` (Number)
+- `hook_id` (Number)
+- `issues_events` (Boolean)
+- `job_events` (Boolean)
+- `merge_requests_events` (Boolean)
+- `note_events` (Boolean)
+- `pipeline_events` (Boolean)
+- `push_events` (Boolean)
+- `push_events_branch_filter` (String)
+- `releases_events` (Boolean)
+- `subgroup_events` (Boolean)
+- `tag_push_events` (Boolean)
+- `token` (String)
+- `url` (String)
+- `wiki_page_events` (Boolean)
+
+
diff --git a/docs/resources/group_hook.md b/docs/resources/group_hook.md
new file mode 100644
index 000000000..6e200fb5f
--- /dev/null
+++ b/docs/resources/group_hook.md
@@ -0,0 +1,90 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "gitlab_group_hook Resource - terraform-provider-gitlab"
+subcategory: ""
+description: |-
+ The gitlab_group_hook resource allows to manage the lifecycle of a group hook.
+ Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/groups.html#hooks
+---
+
+# gitlab_group_hook (Resource)
+
+The `gitlab_group_hook` resource allows to manage the lifecycle of a group hook.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#hooks)
+
+## Example Usage
+
+```terraform
+resource "gitlab_group_hook" "example" {
+ group = "example/hooked"
+ url = "https://example.com/hook/example"
+ merge_requests_events = true
+}
+
+# Setting all attributes
+resource "gitlab_group_hook" "all_attributes" {
+ group = 1
+ url = "http://example.com"
+ token = "supersecret"
+ enable_ssl_verification = false
+ push_events = true
+ push_events_branch_filter = "devel"
+ issues_events = false
+ confidential_issues_events = false
+ merge_requests_events = true
+ tag_push_events = true
+ note_events = true
+ confidential_note_events = true
+ job_events = true
+ pipeline_events = true
+ wiki_page_events = true
+ deployment_events = true
+ releases_events = true
+ subgroup_events = true
+}
+```
+
+
+## Schema
+
+### Required
+
+- `group` (String) The ID or full path of the group.
+- `url` (String) The url of the hook to invoke.
+
+### Optional
+
+- `confidential_issues_events` (Boolean) Invoke the hook for confidential issues events.
+- `confidential_note_events` (Boolean) Invoke the hook for confidential notes events.
+- `deployment_events` (Boolean) Invoke the hook for deployment events.
+- `enable_ssl_verification` (Boolean) Enable ssl verification when invoking the hook.
+- `issues_events` (Boolean) Invoke the hook for issues events.
+- `job_events` (Boolean) Invoke the hook for job events.
+- `merge_requests_events` (Boolean) Invoke the hook for merge requests.
+- `note_events` (Boolean) Invoke the hook for notes events.
+- `pipeline_events` (Boolean) Invoke the hook for pipeline events.
+- `push_events` (Boolean) Invoke the hook for push events.
+- `push_events_branch_filter` (String) Invoke the hook for push events on matching branches only.
+- `releases_events` (Boolean) Invoke the hook for releases events.
+- `subgroup_events` (Boolean) Invoke the hook for subgroup events.
+- `tag_push_events` (Boolean) Invoke the hook for tag push events.
+- `token` (String, Sensitive) A token to present when invoking the hook. The token is not available for imported resources.
+- `wiki_page_events` (Boolean) Invoke the hook for wiki page events.
+
+### Read-Only
+
+- `group_id` (Number) The id of the group for the hook.
+- `hook_id` (Number) The id of the group hook.
+- `id` (String) The ID of this resource.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# A GitLab Group Hook can be imported using a key composed of `:`, e.g.
+terraform import gitlab_group_hook.example "12345:1"
+
+# NOTE: the `token` resource attribute is not available for imported resources as this information cannot be read from the GitLab API.
+```
diff --git a/examples/data-sources/gitlab_group_hook/data-source.tf b/examples/data-sources/gitlab_group_hook/data-source.tf
new file mode 100644
index 000000000..718902f9b
--- /dev/null
+++ b/examples/data-sources/gitlab_group_hook/data-source.tf
@@ -0,0 +1,8 @@
+data "gitlab_group" "example" {
+ id = "foo/bar/baz"
+}
+
+data "gitlab_group_hook" "example" {
+ group = data.gitlab_group.example.id
+ hook_id = 1
+}
diff --git a/examples/data-sources/gitlab_group_hooks/data-source.tf b/examples/data-sources/gitlab_group_hooks/data-source.tf
new file mode 100644
index 000000000..2e1cf9d7b
--- /dev/null
+++ b/examples/data-sources/gitlab_group_hooks/data-source.tf
@@ -0,0 +1,7 @@
+data "gitlab_group" "example" {
+ id = "foo/bar/baz"
+}
+
+data "gitlab_group_hooks" "examples" {
+ group = data.gitlab_group.example.id
+}
diff --git a/examples/resources/gitlab_group_hook/import.sh b/examples/resources/gitlab_group_hook/import.sh
new file mode 100644
index 000000000..ea0f7db64
--- /dev/null
+++ b/examples/resources/gitlab_group_hook/import.sh
@@ -0,0 +1,4 @@
+# A GitLab Group Hook can be imported using a key composed of `:`, e.g.
+terraform import gitlab_group_hook.example "12345:1"
+
+# NOTE: the `token` resource attribute is not available for imported resources as this information cannot be read from the GitLab API.
diff --git a/examples/resources/gitlab_group_hook/resource.tf b/examples/resources/gitlab_group_hook/resource.tf
new file mode 100644
index 000000000..f47f3c983
--- /dev/null
+++ b/examples/resources/gitlab_group_hook/resource.tf
@@ -0,0 +1,27 @@
+resource "gitlab_group_hook" "example" {
+ group = "example/hooked"
+ url = "https://example.com/hook/example"
+ merge_requests_events = true
+}
+
+# Setting all attributes
+resource "gitlab_group_hook" "all_attributes" {
+ group = 1
+ url = "http://example.com"
+ token = "supersecret"
+ enable_ssl_verification = false
+ push_events = true
+ push_events_branch_filter = "devel"
+ issues_events = false
+ confidential_issues_events = false
+ merge_requests_events = true
+ tag_push_events = true
+ note_events = true
+ confidential_note_events = true
+ job_events = true
+ pipeline_events = true
+ wiki_page_events = true
+ deployment_events = true
+ releases_events = true
+ subgroup_events = true
+}
diff --git a/internal/provider/data_source_gitlab_group_hook.go b/internal/provider/data_source_gitlab_group_hook.go
new file mode 100644
index 000000000..ed060c1e4
--- /dev/null
+++ b/internal/provider/data_source_gitlab_group_hook.go
@@ -0,0 +1,39 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/xanzy/go-gitlab"
+)
+
+var _ = registerDataSource("gitlab_group_hook", func() *schema.Resource {
+ return &schema.Resource{
+ Description: `The ` + "`gitlab_group_hook`" + ` data source allows to retrieve details about a hook in a group.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#get-group-hook)`,
+
+ ReadContext: dataSourceGitlabGroupHookRead,
+ Schema: datasourceSchemaFromResourceSchema(gitlabGroupHookSchema(), []string{"group", "hook_id"}, nil),
+ }
+})
+
+func dataSourceGitlabGroupHookRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*gitlab.Client)
+ group := d.Get("group").(string)
+ hookID := d.Get("hook_id").(int)
+
+ hook, _, err := client.Groups.GetGroupHook(group, hookID, gitlab.WithContext(ctx))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId(fmt.Sprintf("%s:%d", group, hookID))
+ stateMap := gitlabGroupHookToStateMap(group, hook)
+ if err := setStateMapInResourceData(stateMap, d); err != nil {
+ return diag.FromErr(err)
+ }
+ return nil
+}
diff --git a/internal/provider/data_source_gitlab_group_hook_test.go b/internal/provider/data_source_gitlab_group_hook_test.go
new file mode 100644
index 000000000..26d5ac074
--- /dev/null
+++ b/internal/provider/data_source_gitlab_group_hook_test.go
@@ -0,0 +1,37 @@
+//go:build acceptance
+// +build acceptance
+
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+)
+
+func TestAccDataSourceGitlabGroupHook_basic(t *testing.T) {
+ testAccCheckEE(t)
+
+ testGroup := testAccCreateGroups(t, 1)[0]
+ testHook := testAccCreateGroupHooks(t, testGroup.ID, 1)[0]
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ data "gitlab_group_hook" "this" {
+ group = "%s"
+ hook_id = %d
+ }
+ `, testGroup.FullPath, testHook.ID),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.gitlab_group_hook.this", "hook_id", fmt.Sprintf("%d", testHook.ID)),
+ resource.TestCheckResourceAttr("data.gitlab_group_hook.this", "group_id", fmt.Sprintf("%d", testGroup.ID)),
+ resource.TestCheckResourceAttr("data.gitlab_group_hook.this", "url", testHook.URL),
+ ),
+ },
+ },
+ })
+}
diff --git a/internal/provider/data_source_gitlab_group_hooks.go b/internal/provider/data_source_gitlab_group_hooks.go
new file mode 100644
index 000000000..486bb13ce
--- /dev/null
+++ b/internal/provider/data_source_gitlab_group_hooks.go
@@ -0,0 +1,69 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/xanzy/go-gitlab"
+)
+
+var _ = registerDataSource("gitlab_group_hooks", func() *schema.Resource {
+ return &schema.Resource{
+ Description: `The ` + "`gitlab_group_hooks`" + ` data source allows to retrieve details about hooks in a group.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#list-group-hooks)`,
+
+ ReadContext: dataSourceGitlabGroupHooksRead,
+ Schema: map[string]*schema.Schema{
+ "group": {
+ Description: "The ID or full path of the group.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "hooks": {
+ Description: "The list of hooks.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: datasourceSchemaFromResourceSchema(gitlabGroupHookSchema(), nil, nil),
+ },
+ },
+ },
+ }
+})
+
+func dataSourceGitlabGroupHooksRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*gitlab.Client)
+
+ group := d.Get("group").(string)
+ options := gitlab.ListGroupHooksOptions{
+ PerPage: 20,
+ Page: 1,
+ }
+
+ var hooks []*gitlab.GroupHook
+ for options.Page != 0 {
+ paginatedHooks, resp, err := client.Groups.ListGroupHooks(group, &options, gitlab.WithContext(ctx))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ hooks = append(hooks, paginatedHooks...)
+ options.Page = resp.NextPage
+ }
+
+ d.SetId(group)
+ if err := d.Set("hooks", flattenGitlabGroupHooks(group, hooks)); err != nil {
+ return diag.Errorf("failed to set hooks to state: %v", err)
+ }
+
+ return nil
+}
+
+func flattenGitlabGroupHooks(group string, hooks []*gitlab.GroupHook) (values []map[string]interface{}) {
+ for _, hook := range hooks {
+ values = append(values, gitlabGroupHookToStateMap(group, hook))
+ }
+ return values
+}
diff --git a/internal/provider/data_source_gitlab_group_hooks_test.go b/internal/provider/data_source_gitlab_group_hooks_test.go
new file mode 100644
index 000000000..d50182d2b
--- /dev/null
+++ b/internal/provider/data_source_gitlab_group_hooks_test.go
@@ -0,0 +1,36 @@
+//go:build acceptance
+// +build acceptance
+
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+)
+
+func TestAccDataSourceGitlabGroupHooks_basic(t *testing.T) {
+ testAccCheckEE(t)
+
+ testGroup := testAccCreateGroups(t, 1)[0]
+ testHooks := testAccCreateGroupHooks(t, testGroup.ID, 25)
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ data "gitlab_group_hooks" "this" {
+ group = "%s"
+ }
+ `, testGroup.FullPath),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.gitlab_group_hooks.this", "hooks.#", fmt.Sprintf("%d", len(testHooks))),
+ resource.TestCheckResourceAttr("data.gitlab_group_hooks.this", "hooks.0.url", testHooks[0].URL),
+ resource.TestCheckResourceAttr("data.gitlab_group_hooks.this", "hooks.1.url", testHooks[1].URL),
+ ),
+ },
+ },
+ })
+}
diff --git a/internal/provider/helper_test.go b/internal/provider/helper_test.go
index 5d524c987..fc77a7bd7 100644
--- a/internal/provider/helper_test.go
+++ b/internal/provider/helper_test.go
@@ -249,6 +249,22 @@ func testAccCreateSubGroups(t *testing.T, parentGroup *gitlab.Group, n int) []*g
return groups
}
+func testAccCreateGroupHooks(t *testing.T, gid interface{}, n int) []*gitlab.GroupHook {
+ t.Helper()
+
+ var hooks []*gitlab.GroupHook
+ for i := 0; i < n; i++ {
+ hook, _, err := testGitlabClient.Groups.AddGroupHook(gid, &gitlab.AddGroupHookOptions{
+ URL: gitlab.String(fmt.Sprintf("https://%s.com", acctest.RandomWithPrefix("acctest"))),
+ })
+ if err != nil {
+ t.Fatalf("could not create group hook: %v", err)
+ }
+ hooks = append(hooks, hook)
+ }
+ return hooks
+}
+
// testAccCreateBranches is a test helper for creating a specified number of branches.
// It assumes the project will be destroyed at the end of the test and will not cleanup created branches.
func testAccCreateBranches(t *testing.T, project *gitlab.Project, n int) []*gitlab.Branch {
diff --git a/internal/provider/resource_gitlab_group_hook.go b/internal/provider/resource_gitlab_group_hook.go
new file mode 100644
index 000000000..ad124f2b6
--- /dev/null
+++ b/internal/provider/resource_gitlab_group_hook.go
@@ -0,0 +1,167 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "strconv"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ gitlab "github.com/xanzy/go-gitlab"
+)
+
+var _ = registerResource("gitlab_group_hook", func() *schema.Resource {
+ return &schema.Resource{
+ Description: `The ` + "`" + `gitlab_group_hook` + "`" + ` resource allows to manage the lifecycle of a group hook.
+
+**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#hooks)`,
+
+ CreateContext: resourceGitlabGroupHookCreate,
+ ReadContext: resourceGitlabGroupHookRead,
+ UpdateContext: resourceGitlabGroupHookUpdate,
+ DeleteContext: resourceGitlabGroupHookDelete,
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+ Schema: gitlabGroupHookSchema(),
+ }
+})
+
+func resourceGitlabGroupHookCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(*gitlab.Client)
+ group := d.Get("group").(string)
+ options := &gitlab.AddGroupHookOptions{
+ URL: gitlab.String(d.Get("url").(string)),
+ PushEvents: gitlab.Bool(d.Get("push_events").(bool)),
+ PushEventsBranchFilter: gitlab.String(d.Get("push_events_branch_filter").(string)),
+ IssuesEvents: gitlab.Bool(d.Get("issues_events").(bool)),
+ ConfidentialIssuesEvents: gitlab.Bool(d.Get("confidential_issues_events").(bool)),
+ MergeRequestsEvents: gitlab.Bool(d.Get("merge_requests_events").(bool)),
+ TagPushEvents: gitlab.Bool(d.Get("tag_push_events").(bool)),
+ NoteEvents: gitlab.Bool(d.Get("note_events").(bool)),
+ ConfidentialNoteEvents: gitlab.Bool(d.Get("confidential_note_events").(bool)),
+ JobEvents: gitlab.Bool(d.Get("job_events").(bool)),
+ PipelineEvents: gitlab.Bool(d.Get("pipeline_events").(bool)),
+ WikiPageEvents: gitlab.Bool(d.Get("wiki_page_events").(bool)),
+ DeploymentEvents: gitlab.Bool(d.Get("deployment_events").(bool)),
+ ReleasesEvents: gitlab.Bool(d.Get("releases_events").(bool)),
+ SubGroupEvents: gitlab.Bool(d.Get("subgroup_events").(bool)),
+ EnableSSLVerification: gitlab.Bool(d.Get("enable_ssl_verification").(bool)),
+ }
+
+ if v, ok := d.GetOk("token"); ok {
+ options.Token = gitlab.String(v.(string))
+ }
+
+ log.Printf("[DEBUG] create gitlab group hook %q", *options.URL)
+
+ hook, _, err := client.Groups.AddGroupHook(group, options, gitlab.WithContext(ctx))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId(resourceGitlabGroupHookBuildID(group, hook.ID))
+ d.Set("token", options.Token)
+
+ return resourceGitlabGroupHookRead(ctx, d, meta)
+}
+
+func resourceGitlabGroupHookRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ group, hookID, err := resourceGitlabGroupHookParseID(d.Id())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ log.Printf("[DEBUG] read gitlab group hook %s/%d", group, hookID)
+
+ client := meta.(*gitlab.Client)
+ hook, _, err := client.Groups.GetGroupHook(group, hookID, gitlab.WithContext(ctx))
+ if err != nil {
+ if is404(err) {
+ log.Printf("[DEBUG] gitlab group hook not found %s/%d, removing from state", group, hookID)
+ d.SetId("")
+ return nil
+ }
+ return diag.FromErr(err)
+ }
+
+ stateMap := gitlabGroupHookToStateMap(group, hook)
+ if err = setStateMapInResourceData(stateMap, d); err != nil {
+ return diag.FromErr(err)
+ }
+ return nil
+}
+
+func resourceGitlabGroupHookUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ group, hookID, err := resourceGitlabGroupHookParseID(d.Id())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ client := meta.(*gitlab.Client)
+ options := &gitlab.EditGroupHookOptions{
+ URL: gitlab.String(d.Get("url").(string)),
+ PushEvents: gitlab.Bool(d.Get("push_events").(bool)),
+ PushEventsBranchFilter: gitlab.String(d.Get("push_events_branch_filter").(string)),
+ IssuesEvents: gitlab.Bool(d.Get("issues_events").(bool)),
+ ConfidentialIssuesEvents: gitlab.Bool(d.Get("confidential_issues_events").(bool)),
+ MergeRequestsEvents: gitlab.Bool(d.Get("merge_requests_events").(bool)),
+ TagPushEvents: gitlab.Bool(d.Get("tag_push_events").(bool)),
+ NoteEvents: gitlab.Bool(d.Get("note_events").(bool)),
+ ConfidentialNoteEvents: gitlab.Bool(d.Get("confidential_note_events").(bool)),
+ JobEvents: gitlab.Bool(d.Get("job_events").(bool)),
+ PipelineEvents: gitlab.Bool(d.Get("pipeline_events").(bool)),
+ WikiPageEvents: gitlab.Bool(d.Get("wiki_page_events").(bool)),
+ DeploymentEvents: gitlab.Bool(d.Get("deployment_events").(bool)),
+ ReleasesEvents: gitlab.Bool(d.Get("releases_events").(bool)),
+ SubGroupEvents: gitlab.Bool(d.Get("subgroup_events").(bool)),
+ EnableSSLVerification: gitlab.Bool(d.Get("enable_ssl_verification").(bool)),
+ }
+
+ if d.HasChange("token") {
+ options.Token = gitlab.String(d.Get("token").(string))
+ }
+
+ log.Printf("[DEBUG] update gitlab group hook %s", d.Id())
+
+ _, _, err = client.Groups.EditGroupHook(group, hookID, options, gitlab.WithContext(ctx))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ return resourceGitlabGroupHookRead(ctx, d, meta)
+}
+
+func resourceGitlabGroupHookDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ group, hookID, err := resourceGitlabGroupHookParseID(d.Id())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ log.Printf("[DEBUG] Delete gitlab group hook %s/%d", group, hookID)
+
+ client := meta.(*gitlab.Client)
+ _, err = client.Groups.DeleteGroupHook(group, hookID, gitlab.WithContext(ctx))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ return nil
+}
+
+func resourceGitlabGroupHookBuildID(group string, agentID int) string {
+ return fmt.Sprintf("%s:%d", group, agentID)
+}
+
+func resourceGitlabGroupHookParseID(id string) (string, int, error) {
+ groupID, rawHookID, err := parseTwoPartID(id)
+ if err != nil {
+ return "", 0, err
+ }
+
+ hookID, err := strconv.Atoi(rawHookID)
+ if err != nil {
+ return "", 0, err
+ }
+
+ return groupID, hookID, nil
+}
diff --git a/internal/provider/resource_gitlab_group_hook_test.go b/internal/provider/resource_gitlab_group_hook_test.go
new file mode 100644
index 000000000..b3885435b
--- /dev/null
+++ b/internal/provider/resource_gitlab_group_hook_test.go
@@ -0,0 +1,113 @@
+//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 TestAccGitlabGroupHook_basic(t *testing.T) {
+ testAccCheckEE(t)
+
+ testGroup := testAccCreateGroups(t, 1)[0]
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ CheckDestroy: testAccCheckGitlabGroupHookDestroy,
+ Steps: []resource.TestStep{
+ // Create a Group Hook with required attributes only
+ {
+ Config: fmt.Sprintf(`
+ resource "gitlab_group_hook" "this" {
+ group = "%s"
+ url = "http://example.com"
+ }
+ `, testGroup.FullPath),
+ },
+ // Verify Import
+ {
+ ResourceName: "gitlab_group_hook.this",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"token"},
+ },
+ // Update Group Hook to set all attributes
+ {
+ Config: fmt.Sprintf(`
+ resource "gitlab_group_hook" "this" {
+ group = "%s"
+ url = "http://example.com"
+
+ token = "supersecret"
+ enable_ssl_verification = false
+ push_events = true
+ push_events_branch_filter = "devel"
+ issues_events = false
+ confidential_issues_events = false
+ merge_requests_events = true
+ tag_push_events = true
+ note_events = true
+ confidential_note_events = true
+ job_events = true
+ pipeline_events = true
+ wiki_page_events = true
+ deployment_events = true
+ releases_events = true
+ subgroup_events = true
+ }
+ `, testGroup.FullPath),
+ },
+ // Verify Import
+ {
+ ResourceName: "gitlab_group_hook.this",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"token"},
+ },
+ // Update Group Hook to defaults again
+ {
+ Config: fmt.Sprintf(`
+ resource "gitlab_group_hook" "this" {
+ group = "%s"
+ url = "http://example.com"
+ }
+ `, testGroup.FullPath),
+ },
+ // Verify Import
+ {
+ ResourceName: "gitlab_group_hook.this",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"token"},
+ },
+ },
+ })
+}
+
+func testAccCheckGitlabGroupHookDestroy(s *terraform.State) error {
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "gitlab_group_hook" {
+ continue
+ }
+
+ group, hookID, err := resourceGitlabGroupHookParseID(rs.Primary.ID)
+ if err != nil {
+ return err
+ }
+
+ _, _, err = testGitlabClient.Groups.GetGroupHook(group, hookID)
+ if err == nil {
+ return fmt.Errorf("Group Hook %d in group %s still exists", hookID, group)
+ }
+ if !is404(err) {
+ return err
+ }
+ return nil
+ }
+ return nil
+}
diff --git a/internal/provider/schema_gitlab_group_hook.go b/internal/provider/schema_gitlab_group_hook.go
new file mode 100644
index 000000000..2b012d99e
--- /dev/null
+++ b/internal/provider/schema_gitlab_group_hook.go
@@ -0,0 +1,150 @@
+package provider
+
+import (
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/xanzy/go-gitlab"
+)
+
+func gitlabGroupHookSchema() map[string]*schema.Schema {
+ return map[string]*schema.Schema{
+ "group": {
+ Description: "The ID or full path of the group.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "group_id": {
+ Description: "The id of the group for the hook.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "hook_id": {
+ Description: "The id of the group hook.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "url": {
+ Description: "The url of the hook to invoke.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "token": {
+ Description: "A token to present when invoking the hook. The token is not available for imported resources.",
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ },
+ "push_events": {
+ Description: "Invoke the hook for push events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ },
+ "push_events_branch_filter": {
+ Description: "Invoke the hook for push events on matching branches only.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "issues_events": {
+ Description: "Invoke the hook for issues events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "confidential_issues_events": {
+ Description: "Invoke the hook for confidential issues events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "merge_requests_events": {
+ Description: "Invoke the hook for merge requests.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "tag_push_events": {
+ Description: "Invoke the hook for tag push events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "note_events": {
+ Description: "Invoke the hook for notes events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "confidential_note_events": {
+ Description: "Invoke the hook for confidential notes events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "job_events": {
+ Description: "Invoke the hook for job events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "pipeline_events": {
+ Description: "Invoke the hook for pipeline events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "wiki_page_events": {
+ Description: "Invoke the hook for wiki page events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "deployment_events": {
+ Description: "Invoke the hook for deployment events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "releases_events": {
+ Description: "Invoke the hook for releases events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "subgroup_events": {
+ Description: "Invoke the hook for subgroup events.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "enable_ssl_verification": {
+ Description: "Enable ssl verification when invoking the hook.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ },
+ }
+}
+
+func gitlabGroupHookToStateMap(group string, hook *gitlab.GroupHook) map[string]interface{} {
+ stateMap := make(map[string]interface{})
+ stateMap["group"] = group
+ stateMap["group_id"] = hook.GroupID
+ stateMap["hook_id"] = hook.ID
+ stateMap["url"] = hook.URL
+ stateMap["push_events"] = hook.PushEvents
+ stateMap["push_events_branch_filter"] = hook.PushEventsBranchFilter
+ stateMap["issues_events"] = hook.IssuesEvents
+ stateMap["confidential_issues_events"] = hook.ConfidentialIssuesEvents
+ stateMap["merge_requests_events"] = hook.MergeRequestsEvents
+ stateMap["tag_push_events"] = hook.TagPushEvents
+ stateMap["note_events"] = hook.NoteEvents
+ stateMap["confidential_note_events"] = hook.ConfidentialNoteEvents
+ stateMap["job_events"] = hook.JobEvents
+ stateMap["pipeline_events"] = hook.PipelineEvents
+ stateMap["wiki_page_events"] = hook.WikiPageEvents
+ stateMap["deployment_events"] = hook.DeploymentEvents
+ stateMap["releases_events"] = hook.ReleasesEvents
+ stateMap["subgroup_events"] = hook.SubGroupEvents
+ stateMap["enable_ssl_verification"] = hook.EnableSSLVerification
+ return stateMap
+}