Repository: ekristen/aws-nuke
Pull request #633: feat: add DocumentDB support
Author: login: pedrokiefer
Labels: name: released
Created: 2025-04-03T19:13:20Z | Merged: 2025-04-28T21:06:05Z



Diff:
diff --git a/docs/resources/access-analyzer.md b/docs/resources/access-analyzer.md
index 43c59ca2..e6e14418 100644
--- a/docs/resources/access-analyzer.md
+++ b/docs/resources/access-analyzer.md
@@ -28,6 +28,7 @@ AWS::AccessAnalyzer::Analyzer
 - `ARN`: The ARN of the analyzer
 - `Name`: The name of the analyzer
 - `Status`: The status of the analyzer
+- `Type`: The type of the analyzer
 - `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
 	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
 
diff --git a/docs/resources/api-gateway-domain-name.md b/docs/resources/api-gateway-domain-name.md
index c4910c30..d19d0eaa 100644
--- a/docs/resources/api-gateway-domain-name.md
+++ b/docs/resources/api-gateway-domain-name.md
@@ -16,6 +16,8 @@ APIGatewayDomainName
 
 - `DomainName`: No Description
 - `DomainNameID`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
 
 !!! note - Using Properties
     Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
diff --git a/docs/resources/doc-db-cluster.md b/docs/resources/doc-db-cluster.md
new file mode 100644
index 00000000..ac6fd36d
--- /dev/null
+++ b/docs/resources/doc-db-cluster.md
@@ -0,0 +1,56 @@
+---
+generated: true
+---
+
+# DocDBCluster
+
+
+## Resource
+
+```text
+DocDBCluster
+```
+
+## Properties
+
+
+- `DeletionProtection`: No Description
+- `ID`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
+## Settings
+
+- `DisableDeletionProtection`
+
+
+### DisableDeletionProtection
+
+!!! note
+    There is currently no description for this setting. Often times settings are fairly self-explanatory. However, we
+    are working on adding descriptions for all settings.
+
+```text
+DisableDeletionProtection
+```
+
+### DependsOn
+
+!!! important - Experimental Feature
+    This resource depends on a resource using the experimental feature. This means that the resource will
+    only be deleted if all the resources of a particular type are deleted first or reach a terminal state.
+
+- [DocDBInstance](./doc-db-instance.md)
+
diff --git a/docs/resources/doc-db-elastic-cluster.md b/docs/resources/doc-db-elastic-cluster.md
new file mode 100644
index 00000000..a48eacf8
--- /dev/null
+++ b/docs/resources/doc-db-elastic-cluster.md
@@ -0,0 +1,31 @@
+---
+generated: true
+---
+
+# DocDBElasticCluster
+
+
+## Resource
+
+```text
+DocDBElasticCluster
+```
+
+## Properties
+
+
+- `ARN`: No Description
+- `Name`: No Description
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/doc-db-event-subscription.md b/docs/resources/doc-db-event-subscription.md
new file mode 100644
index 00000000..00b8363c
--- /dev/null
+++ b/docs/resources/doc-db-event-subscription.md
@@ -0,0 +1,38 @@
+---
+generated: true
+---
+
+# DocDBEventSubscription
+
+
+## Resource
+
+```text
+DocDBEventSubscription
+```
+
+## Properties
+
+
+- `ARN`: No Description
+- `Enabled`: No Description
+- `EventCategories`: No Description
+- `Name`: No Description
+- `SnsTopicArn`: No Description
+- `SourceType`: No Description
+- `Status`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/doc-db-instance.md b/docs/resources/doc-db-instance.md
new file mode 100644
index 00000000..e295ac47
--- /dev/null
+++ b/docs/resources/doc-db-instance.md
@@ -0,0 +1,32 @@
+---
+generated: true
+---
+
+# DocDBInstance
+
+
+## Resource
+
+```text
+DocDBInstance
+```
+
+## Properties
+
+
+- `Identifier`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/doc-db-parameter-group.md b/docs/resources/doc-db-parameter-group.md
new file mode 100644
index 00000000..a8c4fab5
--- /dev/null
+++ b/docs/resources/doc-db-parameter-group.md
@@ -0,0 +1,32 @@
+---
+generated: true
+---
+
+# DocDBParameterGroup
+
+
+## Resource
+
+```text
+DocDBParameterGroup
+```
+
+## Properties
+
+
+- `Name`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/doc-db-snapshot.md b/docs/resources/doc-db-snapshot.md
new file mode 100644
index 00000000..4f1c0343
--- /dev/null
+++ b/docs/resources/doc-db-snapshot.md
@@ -0,0 +1,37 @@
+---
+generated: true
+---
+
+# DocDBSnapshot
+
+
+## Resource
+
+```text
+DocDBSnapshot
+```
+
+## Properties
+
+
+- `ARN`: No Description
+- `AvailabilityZones`: No Description
+- `Identifier`: No Description
+- `SnapshotCreateTime`: No Description
+- `SnapshotType`: No Description
+- `Status`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/doc-db-subnet-group.md b/docs/resources/doc-db-subnet-group.md
new file mode 100644
index 00000000..a280d403
--- /dev/null
+++ b/docs/resources/doc-db-subnet-group.md
@@ -0,0 +1,32 @@
+---
+generated: true
+---
+
+# DocDBSubnetGroup
+
+
+## Resource
+
+```text
+DocDBSubnetGroup
+```
+
+## Properties
+
+
+- `Name`: No Description
+- `tag:<key>:`: This resource has tags with property `Tags`. These are key/value pairs that are
+	added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) 
+
+!!! note - Using Properties
+    Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
+    names to write filters for what you want to **keep** and omit from the nuke process.
+
+### String Property
+
+The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all
+resources support properties. To write a filter against the string representation, simply omit the `property` field in
+the filter.
+
+The string value is always what is used in the output of the log format when a resource is identified.
+
diff --git a/docs/resources/ecs-task-definition.md b/docs/resources/ecs-task-definition.md
index 7a0abd49..5058bbda 100644
--- a/docs/resources/ecs-task-definition.md
+++ b/docs/resources/ecs-task-definition.md
@@ -14,7 +14,8 @@ ECSTaskDefinition
 ## Properties
 
 
-- `ARN`: No Description
+- `Name`: No Description
+- `Status`: No Description
 
 !!! note - Using Properties
     Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property
diff --git a/go.mod b/go.mod
index 1f2d2b3b..05e816d9 100644
--- a/go.mod
+++ b/go.mod
@@ -45,6 +45,8 @@ require (
 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27 // indirect
+	github.com/aws/aws-sdk-go-v2/service/docdb v1.41.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.15.2 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
diff --git a/go.sum b/go.sum
index 84a63e38..070900f2 100644
--- a/go.sum
+++ b/go.sum
@@ -30,6 +30,10 @@ github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.11 h1:azwNdO35Sp6EtX1RTi+g
 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.11/go.mod h1:nr2/4ch+huedh56oGZClTeCVENvMBqeEkzkObAFCDQM=
 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.12 h1:GEdQ1mVjgYH/c7vKrt+rrPS8Ax3wgqOHOBudPE7UIJs=
 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.12/go.mod h1:nr2/4ch+huedh56oGZClTeCVENvMBqeEkzkObAFCDQM=
+github.com/aws/aws-sdk-go-v2/service/docdb v1.41.1 h1:KxSpigaQoHsAcRHuhcFAtqishNM/waI2iaU5Zf8hEYg=
+github.com/aws/aws-sdk-go-v2/service/docdb v1.41.1/go.mod h1:Ft+c7KOTOwfkPKQrPRm5wfEFWXq9oHtFi0yGszwYAgg=
+github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.15.2 h1:2J/DFkFyag8lMMafxZWccTlkEa+IP3D1jvKbBOVNS20=
+github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.15.2/go.mod h1:1eCPm+lTHEN641XC1gE+YAGIVg3ME+MebW9Dc+qeiOA=
 github.com/aws/aws-sdk-go-v2/service/dsql v1.1.1 h1:h2YbGqsnzL36l2B/Bd/dvrHfrKo/b7HVpZykL3yIQn8=
 github.com/aws/aws-sdk-go-v2/service/dsql v1.1.1/go.mod h1:StDU/D7R42LhrKp24PGzvxyKjjDm0lwo9JMwuy2qbo4=
 github.com/aws/aws-sdk-go-v2/service/ecs v1.54.2 h1:euy6eWxHp2mLxA1OqQcBFk5vEuXC1UqZL0x9XPlmxns=
diff --git a/mkdocs.yml b/mkdocs.yml
index dc948556..2d7e8e5f 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -276,6 +276,13 @@ nav:
     - Dax Subnet Group: resources/dax-subnet-group.md
     - Device Farm Project: resources/device-farm-project.md
     - Directory Service Directory: resources/directory-service-directory.md
+    - Doc Db Cluster: resources/doc-db-cluster.md
+    - Doc Db Elastic Cluster: resources/doc-db-elastic-cluster.md
+    - Doc Db Event Subscription: resources/doc-db-event-subscription.md
+    - Doc Db Instance: resources/doc-db-instance.md
+    - Doc Db Parameter Group: resources/doc-db-parameter-group.md
+    - Doc Db Snapshot: resources/doc-db-snapshot.md
+    - Doc Db Subnet Group: resources/doc-db-subnet-group.md
     - Dsql Cluster: resources/dsql-cluster.md
     - Dynamo Db Backup: resources/dynamo-db-backup.md
     - Dynamo Db Table Item: resources/dynamo-db-table-item.md
diff --git a/resources/docdb-cluster.go b/resources/docdb-cluster.go
new file mode 100644
index 00000000..9cf4daa0
--- /dev/null
+++ b/resources/docdb-cluster.go
@@ -0,0 +1,112 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	libsettings "github.com/ekristen/libnuke/pkg/settings"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+var DocDBEmptyTags = []docdbtypes.Tag{}
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+		DependsOn: []string{
+			DocDBInstanceResource,
+		},
+		Settings: []string{
+			"DisableDeletionProtection",
+		},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	params := &docdb.DescribeDBClustersInput{
+		Filters: []docdbtypes.Filter{
+			{
+				Name:   aws.String("engine"),
+				Values: []string{"docdb"},
+			},
+		},
+	}
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, params)
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBCluster{
+				svc:                svc,
+				ID:                 page.DBClusters[i].DBClusterIdentifier,
+				DeletionProtection: page.DBClusters[i].DeletionProtection,
+				Tags:               tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBCluster struct {
+	svc      *docdb.Client
+	settings *libsettings.Setting
+
+	ID                 *string
+	DeletionProtection *bool
+	Tags               []docdbtypes.Tag
+}
+
+func (r *DocDBCluster) Settings(settings *libsettings.Setting) {
+	r.settings = settings
+}
+
+func (r *DocDBCluster) Remove(ctx context.Context) error {
+	if r.settings.GetBool("DisableDeletionProtection") && (r.DeletionProtection != nil && *r.DeletionProtection) {
+		_, err := r.svc.ModifyDBCluster(ctx, &docdb.ModifyDBClusterInput{
+			DBClusterIdentifier: r.ID,
+			DeletionProtection:  aws.Bool(false),
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err := r.svc.DeleteDBCluster(ctx, &docdb.DeleteDBClusterInput{
+		DBClusterIdentifier: r.ID,
+		SkipFinalSnapshot:   aws.Bool(true),
+	})
+	return err
+}
+
+func (r *DocDBCluster) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-elastic-cluster.go b/resources/docdb-elastic-cluster.go
new file mode 100644
index 00000000..f1f1910e
--- /dev/null
+++ b/resources/docdb-elastic-cluster.go
@@ -0,0 +1,65 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/docdbelastic"
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+)
+
+const DocDBElasticClusterResource = "DocDBElasticCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBElasticClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBElasticCluster{},
+		Lister:   &DocDBElasticClusterLister{},
+	})
+}
+
+type DocDBElasticClusterLister struct{}
+
+func (l *DocDBElasticClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdbelastic.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdbelastic.NewListClustersPaginator(svc, &docdbelastic.ListClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, cluster := range page.Clusters {
+			resources = append(resources, &DocDBElasticCluster{
+				svc:  svc,
+				ARN:  cluster.ClusterArn,
+				Name: cluster.ClusterName,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBElasticCluster struct {
+	svc *docdbelastic.Client
+
+	ARN  *string
+	Name *string
+}
+
+func (r *DocDBElasticCluster) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteCluster(ctx, &docdbelastic.DeleteClusterInput{
+		ClusterArn: r.ARN,
+	})
+	return err
+}
+
+func (r *DocDBElasticCluster) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-eventsubscription.go b/resources/docdb-eventsubscription.go
new file mode 100644
index 00000000..a10e186d
--- /dev/null
+++ b/resources/docdb-eventsubscription.go
@@ -0,0 +1,86 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+)
+
+const DocDBEventSubscriptionResource = "DocDBEventSubscription"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBEventSubscriptionResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBEventSubscription{},
+		Lister:   &DocDBEventSubscriptionLister{},
+	})
+}
+
+type DocDBEventSubscriptionLister struct{}
+
+func (l *DocDBEventSubscriptionLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeEventSubscriptionsPaginator(svc, &docdb.DescribeEventSubscriptionsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, subscription := range page.EventSubscriptionsList {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: subscription.CustSubscriptionId,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBEventSubscription{
+				svc:             svc,
+				ARN:             subscription.EventSubscriptionArn,
+				Name:            subscription.CustSubscriptionId,
+				SnsTopicArn:     subscription.SnsTopicArn,
+				SourceType:      subscription.SourceType,
+				Status:          subscription.Status,
+				EventCategories: subscription.EventCategoriesList,
+				Enabled:         subscription.Enabled,
+				Tags:            tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBEventSubscription struct {
+	svc *docdb.Client
+
+	ARN             *string
+	Name            *string
+	SnsTopicArn     *string
+	SourceType      *string
+	Status          *string
+	EventCategories []string
+	Enabled         *bool
+	Tags            []docdbtypes.Tag
+}
+
+func (r *DocDBEventSubscription) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteEventSubscription(ctx, &docdb.DeleteEventSubscriptionInput{
+		SubscriptionName: r.Name,
+	})
+	return err
+}
+
+func (r *DocDBEventSubscription) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-instance.go b/resources/docdb-instance.go
new file mode 100644
index 00000000..31ba2814
--- /dev/null
+++ b/resources/docdb-instance.go
@@ -0,0 +1,85 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBInstanceResource = "DocDBInstance"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBInstanceResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBInstance{},
+		Lister:   &DocDBInstanceLister{},
+	})
+}
+
+type DocDBInstanceLister struct{}
+
+func (l *DocDBInstanceLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	params := &docdb.DescribeDBInstancesInput{
+		Filters: []docdbtypes.Filter{
+			{
+				Name:   aws.String("engine"),
+				Values: []string{"docdb"},
+			},
+		},
+	}
+
+	paginator := docdb.NewDescribeDBInstancesPaginator(svc, params)
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBInstances); i++ {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBInstances[i].DBInstanceArn,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBInstance{
+				svc:        svc,
+				Identifier: page.DBInstances[i].DBInstanceIdentifier,
+				Tags:       tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBInstance struct {
+	svc *docdb.Client
+
+	Identifier *string
+	Tags       []docdbtypes.Tag
+}
+
+func (r *DocDBInstance) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteDBInstance(ctx, &docdb.DeleteDBInstanceInput{
+		DBInstanceIdentifier: r.Identifier,
+	})
+	return err
+}
+
+func (r *DocDBInstance) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-parametergroup.go b/resources/docdb-parametergroup.go
new file mode 100644
index 00000000..b79a3bc2
--- /dev/null
+++ b/resources/docdb-parametergroup.go
@@ -0,0 +1,85 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBParameterGroupResource = "DocDBParameterGroup"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBParameterGroupResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBParameterGroup{},
+		Lister:   &DocDBParameterGroupLister{},
+	})
+}
+
+type DocDBParameterGroupLister struct{}
+
+func (l *DocDBParameterGroupLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	params := &docdb.DescribeDBClusterParameterGroupsInput{
+		Filters: []docdbtypes.Filter{
+			{
+				Name:   aws.String("engine"),
+				Values: []string{"docdb"},
+			},
+		},
+	}
+
+	paginator := docdb.NewDescribeDBClusterParameterGroupsPaginator(svc, params)
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, paramGroup := range page.DBClusterParameterGroups {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: paramGroup.DBClusterParameterGroupName,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBParameterGroup{
+				svc:  svc,
+				Name: paramGroup.DBClusterParameterGroupName,
+				Tags: tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBParameterGroup struct {
+	svc *docdb.Client
+
+	Name *string
+	Tags []docdbtypes.Tag
+}
+
+func (r *DocDBParameterGroup) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteDBClusterParameterGroup(ctx, &docdb.DeleteDBClusterParameterGroupInput{
+		DBClusterParameterGroupName: r.Name,
+	})
+	return err
+}
+
+func (r *DocDBParameterGroup) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-snapshots.go b/resources/docdb-snapshots.go
new file mode 100644
index 00000000..516f52d5
--- /dev/null
+++ b/resources/docdb-snapshots.go
@@ -0,0 +1,106 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBSnapshotResource = "DocDBSnapshot"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSnapshotResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSnapshot{},
+		Lister:   &DocDBSnapshotLister{},
+	})
+}
+
+type DocDBSnapshotLister struct{}
+
+func (l *DocDBSnapshotLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	params := &docdb.DescribeDBClusterSnapshotsInput{
+		Filters: []docdbtypes.Filter{
+			{
+				Name:   aws.String("engine"),
+				Values: []string{"docdb"},
+			},
+		},
+	}
+
+	paginator := docdb.NewDescribeDBClusterSnapshotsPaginator(svc, params)
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusterSnapshots); i++ {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBSnapshot{
+				svc:                svc,
+				ARN:                page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+				Identifier:         page.DBClusterSnapshots[i].DBClusterIdentifier,
+				SnapshotType:       page.DBClusterSnapshots[i].SnapshotType,
+				Status:             page.DBClusterSnapshots[i].Status,
+				AvailabilityZones:  page.DBClusterSnapshots[i].AvailabilityZones,
+				SnapshotCreateTime: page.DBClusterSnapshots[i].SnapshotCreateTime,
+				Tags:               tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBSnapshot struct {
+	svc *docdb.Client
+
+	ARN                *string
+	Identifier         *string
+	SnapshotType       *string
+	Status             *string
+	AvailabilityZones  []string
+	SnapshotCreateTime *time.Time
+	Tags               []docdbtypes.Tag
+}
+
+const DocDBAutomatedSnapshot = "automated"
+
+func (r *DocDBSnapshot) Filter() error {
+	if *r.SnapshotType == DocDBAutomatedSnapshot {
+		return fmt.Errorf("cannot delete automated snapshots")
+	}
+	return nil
+}
+
+func (r *DocDBSnapshot) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteDBClusterSnapshot(ctx, &docdb.DeleteDBClusterSnapshotInput{
+		DBClusterSnapshotIdentifier: r.Identifier,
+	})
+	return err
+}
+
+func (r *DocDBSnapshot) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}
diff --git a/resources/docdb-subnet.go b/resources/docdb-subnet.go
new file mode 100644
index 00000000..1329fc78
--- /dev/null
+++ b/resources/docdb-subnet.go
@@ -0,0 +1,73 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+)
+
+const DocDBSubnetGroupResource = "DocDBSubnetGroup"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSubnetGroupResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSubnetGroup{},
+		Lister:   &DocDBSubnetGroupLister{},
+	})
+}
+
+type DocDBSubnetGroupLister struct{}
+
+func (l *DocDBSubnetGroupLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBSubnetGroupsPaginator(svc, &docdb.DescribeDBSubnetGroupsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, subnetGroup := range page.DBSubnetGroups {
+			tagList := DocDBEmptyTags
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: subnetGroup.DBSubnetGroupArn,
+			})
+			if err == nil {
+				tagList = tags.TagList
+			}
+			resources = append(resources, &DocDBSubnetGroup{
+				svc:  svc,
+				Name: subnetGroup.DBSubnetGroupName,
+				Tags: tagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBSubnetGroup struct {
+	svc *docdb.Client
+
+	Name *string
+	Tags []docdbtypes.Tag
+}
+
+func (r *DocDBSubnetGroup) Remove(ctx context.Context) error {
+	_, err := r.svc.DeleteDBSubnetGroup(ctx, &docdb.DeleteDBSubnetGroupInput{
+		DBSubnetGroupName: r.Name,
+	})
+	return err
+}
+
+func (r *DocDBSubnetGroup) Properties() types.Properties {
+	return types.NewPropertiesFromStruct(r)
+}


Reviews:
user: login: giseleflorenco
state: APPROVED
author_association: NONE
submitted_at: 2025-04-03T19:18:27Z
user: login: Liraquin
state: APPROVED
author_association: NONE
submitted_at: 2025-04-03T19:45:46Z
user: login: ekristen
body: I only commented on a few of the resources, but they all have the same comment. 

1. Please use struct fields for every property that you want to add.
2. Please use `types.NewPropertiesFromStruct(r)`
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-03T19:53:23Z
user: login: pedrokiefer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T20:07:27Z
user: login: pedrokiefer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T20:09:42Z
user: login: ekristen
body: @pedrokiefer 

>How granular should the properties be? Should I add more information?

I don't have a rule, I tend to do what would be considered commonly needed properties like CreatedAt, Name, Tags, etc, I think what you have is fine for the moment.

## Docs Generation

```go run tools/generate-docs/main.go --write``` 

You can commit with `git commit -a -m 'docs(document-db): auto-generated'`

This will add all the docs for the new resources, including the properties.
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-03T20:54:14Z
user: login: pedrokiefer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T20:56:34Z
user: login: ekristen
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-03T20:57:20Z
user: login: ekristen
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-03T21:24:45Z
user: login: ekristen
body: Can you also rename `docdbelastic-cluster.go` to `docdb-elastic-cluster.go`
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-04T19:24:20Z

Review comments:
diff_hunk: @@ -0,0 +1,95 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, &docdb.DescribeDBClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err != nil {
+				continue
path: resources/docdb-cluster.go
user: login: ekristen
body: Due to AWS permissions tags could fail but we don't want to prevent resources from being added. The code should have an empty tag list and then populate if the call is successful, log if it is not, and still append the resource.
created_at: 2025-04-03T19:48:40Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,95 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, &docdb.DescribeDBClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBCluster{
+				svc:                svc,
+				ID:                 aws.ToString(page.DBClusters[i].DBClusterIdentifier),
+				deletionProtection: aws.ToBool(page.DBClusters[i].DeletionProtection),
+				tags:               tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBCluster struct {
+	svc                *docdb.Client
+	ID                 string
+	deletionProtection bool
path: resources/docdb-cluster.go
user: login: ekristen
body: Upper casing the properties and then leveraging `types.NewPropertiesFromStruct(r)` ensures properties are documented and automatically put in place in the correct way.
created_at: 2025-04-03T19:50:03Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,95 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, &docdb.DescribeDBClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBCluster{
+				svc:                svc,
+				ID:                 aws.ToString(page.DBClusters[i].DBClusterIdentifier),
+				deletionProtection: aws.ToBool(page.DBClusters[i].DeletionProtection),
+				tags:               tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBCluster struct {
+	svc                *docdb.Client
+	ID                 string
+	deletionProtection bool
+	tags               []docdbtypes.Tag
+}
+
+func (r *DocDBCluster) Remove(ctx context.Context) error {
+	if r.deletionProtection {
+		_, err := r.svc.ModifyDBCluster(ctx, &docdb.ModifyDBClusterInput{
+			DBClusterIdentifier: aws.String(r.ID),
+			DeletionProtection:  aws.Bool(false),
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err := r.svc.DeleteDBCluster(ctx, &docdb.DeleteDBClusterInput{
+		DBClusterIdentifier: aws.String(r.ID),
+		SkipFinalSnapshot:   aws.Bool(true),
+	})
+	return err
+}
+
+func (r *DocDBCluster) Properties() types.Properties {
+	properties := types.NewProperties()
path: resources/docdb-cluster.go
user: login: ekristen
body: this should be `return types.NewPropertiesFromStruct(r)`
created_at: 2025-04-03T19:50:18Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,85 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+)
+
+const DocDBEventSubscriptionResource = "DocDBEventSubscription"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBEventSubscriptionResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBEventSubscription{},
+		Lister:   &DocDBEventSubscriptionLister{},
+	})
+}
+
+type DocDBEventSubscriptionLister struct{}
+
+func (l *DocDBEventSubscriptionLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeEventSubscriptionsPaginator(svc, &docdb.DescribeEventSubscriptionsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, subscription := range page.EventSubscriptionsList {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: subscription.CustSubscriptionId,
+			})
+			if err != nil {
+				continue
path: resources/docdb-eventsubscription.go
user: login: ekristen
body: Same tag comment from above. 
created_at: 2025-04-03T19:50:36Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,85 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+)
+
+const DocDBEventSubscriptionResource = "DocDBEventSubscription"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBEventSubscriptionResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBEventSubscription{},
+		Lister:   &DocDBEventSubscriptionLister{},
+	})
+}
+
+type DocDBEventSubscriptionLister struct{}
+
+func (l *DocDBEventSubscriptionLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeEventSubscriptionsPaginator(svc, &docdb.DescribeEventSubscriptionsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, subscription := range page.EventSubscriptionsList {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: subscription.CustSubscriptionId,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBEventSubscription{
+				svc:          svc,
+				subscription: subscription,
+				tags:         tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBEventSubscription struct {
+	svc          *docdb.Client
+	subscription docdbtypes.EventSubscription
path: resources/docdb-eventsubscription.go
user: login: ekristen
body: I would prefer you add each properties as it's own field instead of an entire object, then `types.NewPropertiesFromStruct` and the automated documentation feature can be used.
created_at: 2025-04-03T19:51:39Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,95 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, &docdb.DescribeDBClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err != nil {
+				continue
path: resources/docdb-cluster.go
user: login: pedrokiefer
body: Makes sense, I will fix it.
created_at: 2025-04-03T20:07:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBClusterResource = "DocDBCluster"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBClusterResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBCluster{},
+		Lister:   &DocDBClusterLister{},
+	})
+}
+
+type DocDBClusterLister struct{}
+
+func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClustersPaginator(svc, &docdb.DescribeDBClustersInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusters); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusters[i].DBClusterArn,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBCluster{
+				svc:                svc,
+				ID:                 aws.ToString(page.DBClusters[i].DBClusterIdentifier),
+				deletionProtection: aws.ToBool(page.DBClusters[i].DeletionProtection),
+				tags:               tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBCluster struct {
+	svc                *docdb.Client
+	ID                 string
+	deletionProtection bool
path: resources/docdb-cluster.go
user: login: pedrokiefer
body: I was looking at `rds-cluster.go` for example and was unsure what was the proper way to handle properties, using a struct is definitely better. 
created_at: 2025-04-03T20:09:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBSnapshotResource = "DocDBSnapshot"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSnapshotResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSnapshot{},
+		Lister:   &DocDBSnapshotLister{},
+	})
+}
+
+type DocDBSnapshotLister struct{}
+
+func (l *DocDBSnapshotLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClusterSnapshotsPaginator(svc, &docdb.DescribeDBClusterSnapshotsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusterSnapshots); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBSnapshot{
+				svc:                svc,
+				ARN:                aws.ToString(page.DBClusterSnapshots[i].DBClusterSnapshotArn),
+				Identifier:         aws.ToString(page.DBClusterSnapshots[i].DBClusterIdentifier),
+				SnapshotType:       aws.ToString(page.DBClusterSnapshots[i].SnapshotType),
+				Status:             aws.ToString(page.DBClusterSnapshots[i].Status),
+				AvailabilityZones:  page.DBClusterSnapshots[i].AvailabilityZones,
+				SnapshotCreateTime: page.DBClusterSnapshots[i].SnapshotCreateTime,
+				Tags:               tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBSnapshot struct {
+	svc *docdb.Client
+
+	ARN                string
path: resources/docdb-snapshots.go
user: login: ekristen
body: Please use pointers, this way we can limit `aws.ToString()` and `aws.String()` conversions. 
created_at: 2025-04-03T20:50:00Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,96 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBSnapshotResource = "DocDBSnapshot"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSnapshotResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSnapshot{},
+		Lister:   &DocDBSnapshotLister{},
+	})
+}
+
+type DocDBSnapshotLister struct{}
+
+func (l *DocDBSnapshotLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClusterSnapshotsPaginator(svc, &docdb.DescribeDBClusterSnapshotsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusterSnapshots); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+			})
+			if err != nil {
+				continue
path: resources/docdb-snapshots.go
user: login: ekristen
body: Don't forget about this tags continue as well.
created_at: 2025-04-03T20:50:11Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,96 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBSnapshotResource = "DocDBSnapshot"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSnapshotResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSnapshot{},
+		Lister:   &DocDBSnapshotLister{},
+	})
+}
+
+type DocDBSnapshotLister struct{}
+
+func (l *DocDBSnapshotLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClusterSnapshotsPaginator(svc, &docdb.DescribeDBClusterSnapshotsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusterSnapshots); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+			})
+			if err != nil {
+				continue
path: resources/docdb-snapshots.go
user: login: pedrokiefer
body: Ops! my bad
created_at: 2025-04-03T20:56:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/docdb"
+	docdbtypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
+
+	"github.com/ekristen/libnuke/pkg/registry"
+	"github.com/ekristen/libnuke/pkg/resource"
+	"github.com/ekristen/libnuke/pkg/types"
+
+	"github.com/ekristen/aws-nuke/v3/pkg/nuke"
+)
+
+const DocDBSnapshotResource = "DocDBSnapshot"
+
+func init() {
+	registry.Register(&registry.Registration{
+		Name:     DocDBSnapshotResource,
+		Scope:    nuke.Account,
+		Resource: &DocDBSnapshot{},
+		Lister:   &DocDBSnapshotLister{},
+	})
+}
+
+type DocDBSnapshotLister struct{}
+
+func (l *DocDBSnapshotLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) {
+	opts := o.(*nuke.ListerOpts)
+	svc := docdb.NewFromConfig(*opts.Config)
+	var resources []resource.Resource
+
+	paginator := docdb.NewDescribeDBClusterSnapshotsPaginator(svc, &docdb.DescribeDBClusterSnapshotsInput{})
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for i := 0; i < len(page.DBClusterSnapshots); i++ {
+			tags, err := svc.ListTagsForResource(ctx, &docdb.ListTagsForResourceInput{
+				ResourceName: page.DBClusterSnapshots[i].DBClusterSnapshotArn,
+			})
+			if err != nil {
+				continue
+			}
+			resources = append(resources, &DocDBSnapshot{
+				svc:                svc,
+				ARN:                aws.ToString(page.DBClusterSnapshots[i].DBClusterSnapshotArn),
+				Identifier:         aws.ToString(page.DBClusterSnapshots[i].DBClusterIdentifier),
+				SnapshotType:       aws.ToString(page.DBClusterSnapshots[i].SnapshotType),
+				Status:             aws.ToString(page.DBClusterSnapshots[i].Status),
+				AvailabilityZones:  page.DBClusterSnapshots[i].AvailabilityZones,
+				SnapshotCreateTime: page.DBClusterSnapshots[i].SnapshotCreateTime,
+				Tags:               tags.TagList,
+			})
+		}
+	}
+	return resources, nil
+}
+
+type DocDBSnapshot struct {
+	svc *docdb.Client
+
+	ARN                string
path: resources/docdb-snapshots.go
user: login: ekristen
body: Almost all responses are pointers and all inputs are pointers, plus it reduces the copy of data less and should be nicer on memory. 
created_at: 2025-04-03T20:57:20Z
author_association: OWNER
diff_hunk: @@ -61,15 +77,20 @@ func (l *DocDBClusterLister) List(ctx context.Context, o interface{}) ([]resourc
 }
 
 type DocDBCluster struct {
-	svc *docdb.Client
+	svc      *docdb.Client
+	settings *libsettings.Setting
 
 	ID                 *string
 	DeletionProtection *bool
 	Tags               []docdbtypes.Tag
 }
 
+func (r *DocDBCluster) Settings(settings *libsettings.Setting) {
+	r.settings = settings
+}
+
 func (r *DocDBCluster) Remove(ctx context.Context) error {
-	if *r.DeletionProtection {
+	if r.settings.GetBool("DisableDeletionProtection") {
path: resources/docdb-cluster.go
user: login: ekristen
body: If you check that `DeletionProtection` is true, then we can avoid an API call that's not necessary
created_at: 2025-04-04T19:23:13Z
author_association: OWNER

PR comments:
user: login: ekristen
created_at: 2025-04-03T19:54:31Z
body: @pedrokiefer thanks for the contribution! I do have a few requests for you to address. 
author_association: OWNER
user: login: pedrokiefer
created_at: 2025-04-03T20:10:33Z
body: > I only commented on a few of the resources, but they all have the same comment.
> 
> 1. Please use struct fields for every property that you want to add.
> 2. Please use `types.NewPropertiesFromStruct(r)`

How granular should the properties be? Should I add more information?
author_association: CONTRIBUTOR
user: login: ekristen
created_at: 2025-04-04T00:57:35Z
body: @pedrokiefer testing it locally, I ran into an issue with `default.neptune1.3` from `DocDBParameterGroup` not being able to be deleted. We'll need a filter for default parameter groups. It looks like prefixed by `default.` would solve it. 
author_association: OWNER
user: login: mdgm88
created_at: 2025-04-04T06:06:11Z
body: The DocumentDB resources should be checked to make sure it doesn't list any Neptune, RDS, Aurora etc. resources. Those should all be filtered out.
author_association: NONE
user: login: ekristen
created_at: 2025-04-04T13:01:07Z
body: @pedrokiefer can you please review the docs for the API endpoints and add parameters to filter out what @mdgm88 called out. Or you will need to implement it in the pagination loops. You can look at Neptune resources to see how we've done this in the past.
author_association: OWNER
user: login: pedrokiefer
created_at: 2025-04-04T13:01:59Z
body: Neptune and DocDB are pretty much the RDS API with some tiny differences. Last time I've tried to implement ABAC for RDS we hit a conflict with DocDB instances, which are basically an RDS instance but doesn't implement the needed tagging for ABAC.

I'll run some tests on a sandbox account. 
author_association: CONTRIBUTOR
user: login: pedrokiefer
created_at: 2025-04-04T19:21:10Z
body: Tested on my sandbox account:
```
INFO[0097] would remove                                  owner=us-east-1 prop:ARN="arn:aws:docdb-elastic:us-east-1:123456789012:cluster/98459782-5b4e-4eec-bcf8-a4f9ff59368a" prop:Name=test-nuke-cluster state=new state_code=0 type=DocDBElasticCluster
INFO[0097] would remove                                  owner=us-east-1 prop:DeletionProtection=true prop:ID=test-nuke-instance state=new state_code=0 type=DocDBCluster
INFO[0097] would remove                                  owner=us-east-1 prop:Name=default-vpc-040a3a3102ba85451 state=new state_code=0 type=DocDBSubnetGroup
INFO[0097] would remove                                  owner=us-east-1 prop:Identifier=test-nuke-instance state=new state_code=0 type=DocDBInstance
INFO[0097] would remove                                  owner=us-east-1 prop:Identifier=test-nuke-instance2 state=new state_code=0 type=DocDBInstance
INFO[0097] would remove                                  owner=us-east-1 prop:Identifier=test-nuke-instance3 state=new state_code=0 type=DocDBInstance
INFO[0097] would remove                                  owner=us-east-1 prop:Name=default.docdb4.0 state=new state_code=0 type=DocDBParameterGroup
INFO[0097] would remove                                  owner=us-east-1 prop:Name=default.docdb5.0 state=new state_code=0 type=DocDBParameterGroup
Scan complete: 8 total, 8 nukeable, 0 filtered.
```

It properly nuked 4 resources and failed 4: default's subnet and parameter group and the cluster with `DeletionProtection=true`.  Adding to my `settings`:
```
  DocDBCluster:
    DisableDeletionProtection: true
```
 made the nuke remove the remaining instance. 
author_association: CONTRIBUTOR
user: login: ekristen
created_at: 2025-04-07T14:26:14Z
body: Almost there @pedrokiefer need the `String()` method on all resources types. Its missing from DocDBSubnetGroup and DocDBInstance and DocDBCluster, please make sure it's added. It should by default rename the `Name` property if it exists, if not then ARN.

Also rename `docdb-snapshots.go` to `docdb-snapshot.go` 
author_association: OWNER
user: login: ekristen
created_at: 2025-04-07T14:41:38Z
body: It also looks like we need to filter out default subnet groups and default parameters from being nuked as it'll throw an error. You can look at how RDS and Neptune do this.
author_association: OWNER
user: login: ekristen
created_at: 2025-04-07T14:43:51Z
body: @pedrokiefer can you also modify rds-instance.go and add the following below the neptune entry.

```
                if ptr.ToString(instance.Engine) == "docdb" {
			opts.Logger.Debug("skipping docdb instance, it is handled by DocDBInstance")
			continue
		}
```

With the additional of the DocDBInstance we need to make sure the RDSInstance doesn't try and remove as well.
author_association: OWNER
user: login: ekristen
created_at: 2025-04-10T14:57:24Z
body: hey @pedrokiefer just following up. Apologies for all the back and forth and I really appreciate your contribution. This is nearly there, just a couple more things. If you'd rather I can merge and fix myself. I'll leave that to you.
author_association: OWNER
user: login: pedrokiefer
created_at: 2025-04-10T19:05:17Z
body: I'll find some time today to do the changes. I has been a really busy week here!
author_association: CONTRIBUTOR
user: login: ekristen
created_at: 2025-04-20T15:53:47Z
body: @pedrokiefer I was on a work trip and on a bit of PTO, I'm back now. I think there's just one minor outstanding issue. I can always merge and finish the implementation if you'd prefer, I leave it to you. Cheers!
author_association: OWNER
user: login: ekristen-dev[bot]
created_at: 2025-04-28T22:48:37Z
body: :tada: This PR is included in version 3.52.0 :tada:

The release is available on [GitHub release](https://github.com/ekristen/aws-nuke/releases/tag/v3.52.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: CONTRIBUTOR
user: login: z0rc
created_at: 2025-05-09T10:23:54Z
body: > @pedrokiefer testing it locally, I ran into an issue with default.neptune1.3 from DocDBParameterGroup not being able to be deleted. We'll need a filter for default parameter groups. It looks like prefixed by default. would solve it.

This wasn't addressed. I'm seeing new error:
```
INFO[1792] failed                                        owner=us-east-2 prop:Name=default.docdb5.0 state=failed state_code=6 type=DocDBParameterGroup
operation error DocDB: DeleteDBClusterParameterGroup, https response error StatusCode: 400, RequestID: 8a66e5bf-be26-4c08-abf7-3c80be0dff7c, InvalidDBParameterGroupState: Default DBClusterParameterGroup cannot be deleted: default.docdb5.0
```
author_association: NONE
user: login: ekristen
created_at: 2025-05-12T14:57:57Z
body: @z0rc yes definitely a bug that needs to be fixed.
author_association: OWNER