Repository: gruntwork-io/cloud-nuke
Pull request #687 (2024-04-22 20:09:03)

Implementation for removing VPC peer connection
<!-- Prepend '[WIP]' to the title if this PR is still a work-in-progress. Remove it when it is ready for review! -->

## Description

Fixes #https://github.com/gruntwork-io/cloud-nuke/issues/443.

<!-- Description of the changes introduced by this PR. -->

## TODOs

Read the [Gruntwork contribution guidelines](https://gruntwork.notion.site/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e).

- [ ] Update the docs.
- [ ] Run the relevant tests successfully, including pre-commit checks.
- [ ] Ensure any 3rd party code adheres with our [license policy](https://www.notion.so/gruntwork/Gruntwork-licenses-and-open-source-usage-policy-f7dece1f780341c7b69c1763f22b1378) or delete this line if its not applicable.
- [ ] Include release notes. If this PR is backward incompatible, include a migration guide.
- [ ] Attention Grunts - if this PR adds support for a new resource, ensure the `nuke_sandbox` and `nuke_phxdevops` jobs in `.circleci/config.yml` have been updated with appropriate exclusions (either directly in the job or via the `.circleci/nuke_config.yml` file) to prevent nuking IAM roles, groups, resources, etc that are important for the test accounts.


## Release Notes (draft)

<!-- One-line description of the PR that can be included in the final release notes. -->
Added / Removed / Updated [X].

### Migration Guide

<!-- Important: If you made any backward incompatible changes, then you must write a migration guide! -->




Patch:
diff --git a/README.md b/README.md
index bcc6096..c260b0b 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,11 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res
 | Route53                 | Hosted Zones                                             |
 | Route53                 | CIDR collections                                         |
 | Route53                 | Traffic Policies                                         |
+| NetworkFirewall         | Network Firewall                                         |
+| NetworkFirewall         | Network Firewall Policy                                  |
+| NetworkFirewall         | Network Firewall Rule Group                              |
+| NetworkFirewall         | Network Firewall TLS inspection configuration            |
+| NetworkFirewall         | Network Firewall Resource Policy 						             |
 
 > **WARNING:** The RDS APIs also interact with neptune and document db resources.
 > Running `cloud-nuke aws --resource-type rds` without a config file will remove any neptune and document db resources
@@ -617,6 +622,11 @@ of the file that are supported are listed here.
 | route53-hosted-zone         | Route53HostedZone            | ✅ (Hosted zone name)                  | ❌                                   | ❌    |    ❌   |
 | route53-cidr-collection     | Route53CIDRCollection        | ✅ (Cidr collection name)              | ❌                                   | ❌    |    ❌   |
 | route53-traffic-policy      | Route53TrafficPolicy         | ✅ (Traffic policy name)               | ❌                                   | ❌    |    ❌   |
+| network-firewall			      | NetworkFirewall 		         | ✅ (Firewall name)             			  | ✅ (First Seen Tag Time)             | ✅    |    ❌   |
+| network-firewall-policy		  | NetworkFirewallPolicy        | ✅ (Firewall Policy name)       			  | ✅ (First Seen Tag Time)             | ✅    |    ❌   |
+| network-firewall-rule-group | NetworkFirewallRuleGroup     | ✅ (Firewall Rule group name)   			  | ✅ (First Seen Tag Time)             | ✅    |    ❌   |
+| network-firewall-tls-config | NetworkFirewallTLSConfig     | ✅ (Firewall TLS config name)   			  | ✅ (First Seen Tag Time)             | ✅    |    ❌   |
+| network-firewall-resource-policy | NetworkFirewallResourcePolicy     | ✅ (Firewall Resource Policy ARN)   			  |  ❌             |  ❌    |    ❌   |
 
 
 ### How to Use
diff --git a/aws/resource_registry.go b/aws/resource_registry.go
index 91e2811..1f646b4 100644
--- a/aws/resource_registry.go
+++ b/aws/resource_registry.go
@@ -134,6 +134,11 @@ func getRegisteredRegionalResources() []AwsResource {
 		&resources.NetworkInterface{},
 		&resources.SecurityGroup{},
 		&resources.NetworkACL{},
+		&resources.NetworkFirewall{},
+		&resources.NetworkFirewallPolicy{},
+		&resources.NetworkFirewallRuleGroup{},
+		&resources.NetworkFirewallTLSConfig{},
+		&resources.NetworkFirewallResourcePolicy{},
 	}
 }
 
diff --git a/aws/resources/ec2_vpc.go b/aws/resources/ec2_vpc.go
index a1ddaff..43278ba 100644
--- a/aws/resources/ec2_vpc.go
+++ b/aws/resources/ec2_vpc.go
@@ -4,16 +4,19 @@ import (
 	"context"
 	cerrors "errors"
 	"fmt"
+	"slices"
 	"strconv"
 	"strings"
 	"time"
 
-	"github.com/aws/aws-sdk-go-v2/aws"
-
 	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/aws/aws-sdk-go/service/elbv2"
+	"github.com/aws/aws-sdk-go/service/elbv2/elbv2iface"
 	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/pterm/pterm"
 
+	"github.com/gruntwork-io/go-commons/retry"
+
 	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/gruntwork-io/cloud-nuke/config"
@@ -64,7 +67,7 @@ func (v *EC2VPCs) getAll(_ context.Context, configObj config.Config) ([]*string,
 			{
 				Name: awsgo.String("is-default"),
 				Values: []*string{
-					aws.String(strconv.FormatBool(configObj.VPC.DefaultOnly)), // convert the bool status into string
+					awsgo.String(strconv.FormatBool(configObj.VPC.DefaultOnly)), // convert the bool status into string
 				},
 			},
 		},
@@ -113,7 +116,7 @@ func (v *EC2VPCs) nukeAll(vpcIds []string) error {
 
 	for _, id := range vpcIds {
 		var err error
-		err = nuke(v.Client, id)
+		err = nuke(v.Client, v.ELBClient, id)
 
 		// Record status of this resource
 		e := report.Entry{
@@ -136,10 +139,30 @@ func (v *EC2VPCs) nukeAll(vpcIds []string) error {
 	return nil
 }
 
-func nuke(client ec2iface.EC2API, vpcID string) error {
+func nuke(client ec2iface.EC2API, elbClient elbv2iface.ELBV2API, vpcID string) error {
+	var err error
 	// Note: order is quite important, otherwise you will encounter dependency violation errors.
+
+	err = nukeAttachedLB(client, elbClient, vpcID)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Error nuking loadbalancer for VPC %s: %s", vpcID, err.Error()))
+		return err
+	}
+
+	err = nukeTargetGroups(elbClient, vpcID)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Error nuking target group for VPC %s: %s", vpcID, err.Error()))
+		return err
+	}
+
+	err = nukeEc2Instances(client, vpcID)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Error nuking instances for VPC %s: %s", vpcID, err.Error()))
+		return err
+	}
+
 	logging.Debug(fmt.Sprintf("Start nuking VPC %s", vpcID))
-	err := nukeDhcpOptions(client, vpcID)
+	err = nukeDhcpOptions(client, vpcID)
 	if err != nil {
 		logging.Debug(fmt.Sprintf("Error cleaning up DHCP Options for VPC %s: %s", vpcID, err.Error()))
 		return err
@@ -169,6 +192,44 @@ func nuke(client ec2iface.EC2API, vpcID string) error {
 		return err
 	}
 
+	// NOTE: Since the network interfaces attached to the load balancer may not be removed immediately after removing the load balancers,
+	// and attempting to remove the internet gateway without waiting for these network interfaces removal will result in an error.
+	// The actual error message states: 'has some mapped public address(es). Please unmap those public address(es) before detaching the gateway.'
+	// Therefore, it is recommended to wait until all the load balancer-related network interfaces are detached and deleted before proceeding.
+	//
+	// Important : The waiting should be happen before nuking the internet gateway
+	err = retry.DoWithRetry(
+		logging.Logger.WithTime(time.Now()),
+		"Waiting for all Network interfaces to be deleted.",
+		// Wait a maximum of 5 minutes: 10 seconds in between, up to 30 times
+		30, 10*time.Second,
+		func() error {
+			interfaces, err := client.DescribeNetworkInterfaces(
+				&ec2.DescribeNetworkInterfacesInput{
+					Filters: []*ec2.Filter{
+						{
+							Name:   awsgo.String("vpc-id"),
+							Values: []*string{awsgo.String(vpcID)},
+						},
+					},
+				},
+			)
+			if err != nil {
+				return errors.WithStackTrace(err)
+			}
+
+			if len(interfaces.NetworkInterfaces) == 0 {
+				return nil
+			}
+
+			return fmt.Errorf("Not all Network interfaces are deleted.")
+		},
+	)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Error waiting up Network interfaces deletion for VPC %s: %s", vpcID, err.Error()))
+		return errors.WithStackTrace(err)
+	}
+
 	err = nukeInternetGateways(client, vpcID)
 	if err != nil {
 		logging.Debug(fmt.Sprintf("Error cleaning up Internet Gateway for VPC %s: %s", vpcID, err.Error()))
@@ -199,6 +260,12 @@ func nuke(client ec2iface.EC2API, vpcID string) error {
 		return err
 	}
 
+	err = nukePeeringConnections(client, vpcID)
+	if err != nil {
+		pterm.Error.Println(fmt.Sprintf("Error deleting VPC Peer connection %s: %s ", vpcID, err))
+		return err
+	}
+
 	err = nukeVpc(client, vpcID)
 	if err != nil {
 		pterm.Error.Println(fmt.Sprintf("Error deleting VPC %s: %s ", vpcID, err))
@@ -210,6 +277,79 @@ func nuke(client ec2iface.EC2API, vpcID string) error {
 	return nil
 }
 
+func nukePeeringConnections(client ec2iface.EC2API, vpcID string) error {
+	logging.Debug(fmt.Sprintf("Finding VPC peering connections to nuke for: %s", vpcID))
+
+	peerConnections := []*string{}
+	vpcIds := []string{vpcID}
+	requesterFilters := []*ec2.Filter{
+		{
+			Name:   awsgo.String("requester-vpc-info.vpc-id"),
+			Values: awsgo.StringSlice(vpcIds),
+		}, {
+			Name:   awsgo.String("status-code"),
+			Values: awsgo.StringSlice([]string{"active"}),
+		},
+	}
+	accepterFilters := []*ec2.Filter{
+		{
+			Name:   awsgo.String("accepter-vpc-info.vpc-id"),
+			Values: awsgo.StringSlice(vpcIds),
+		}, {
+			Name:   awsgo.String("status-code"),
+			Values: awsgo.StringSlice([]string{"active"}),
+		},
+	}
+
+	// check the peering connection as requester
+	err := client.DescribeVpcPeeringConnectionsPages(
+		&ec2.DescribeVpcPeeringConnectionsInput{
+			Filters: requesterFilters,
+		},
+		func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool {
+			for _, connection := range page.VpcPeeringConnections {
+				peerConnections = append(peerConnections, connection.VpcPeeringConnectionId)
+			}
+			return !lastPage
+		},
+	)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe vpc peering connections for vpc as requester: %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+
+	// check the peering connection as accepter
+	err = client.DescribeVpcPeeringConnectionsPages(
+		&ec2.DescribeVpcPeeringConnectionsInput{
+			Filters: accepterFilters,
+		},
+		func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool {
+			for _, connection := range page.VpcPeeringConnections {
+				peerConnections = append(peerConnections, connection.VpcPeeringConnectionId)
+			}
+			return !lastPage
+		},
+	)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe vpc peering connections for vpc as accepter: %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+
+	logging.Debug(fmt.Sprintf("Found %d VPC Peering connections to Nuke.", len(peerConnections)))
+
+	for _, connection := range peerConnections {
+		_, err := client.DeleteVpcPeeringConnection(&ec2.DeleteVpcPeeringConnectionInput{
+			VpcPeeringConnectionId: connection,
+		})
+		if err != nil {
+			logging.Debug(fmt.Sprintf("Failed to delete peering connection for vpc: %s", vpcID))
+			return errors.WithStackTrace(err)
+		}
+	}
+
+	return nil
+}
+
 // nukeVpcInternetGateways
 // This function is specifically for VPCs. It retrieves all the internet gateways attached to the given VPC ID and nuke them
 func nukeInternetGateways(client ec2iface.EC2API, vpcID string) error {
@@ -678,3 +818,156 @@ func nukeVpc(client ec2iface.EC2API, vpcID string) error {
 	logging.Debug(fmt.Sprintf("Successfully deleted VPC %s", vpcID))
 	return nil
 }
+
+func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID string) error {
+	logging.Debug(fmt.Sprintf("Describing load balancers for %s", vpcID))
+
+	// get all loadbalancers
+	output, err := elbclient.DescribeLoadBalancers(nil)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe loadbalancer for %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+	var attachedLoadBalancers []string
+
+	// get a list of load balancers which was attached on the vpc
+	for _, lb := range output.LoadBalancers {
+		if awsgo.StringValue(lb.VpcId) != vpcID {
+			continue
+		}
+
+		attachedLoadBalancers = append(attachedLoadBalancers, awsgo.StringValue(lb.LoadBalancerArn))
+	}
+
+	// check the load-balancers are attached with any vpc-endpoint-service, then nuke them first
+	esoutput, err := client.DescribeVpcEndpointServiceConfigurations(nil)
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe vpc endpoint services for %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+
+	// since we don't want duplicating the endpoint service ids, using a map here
+	var nukableEndpointServices = make(map[*string]struct{})
+	for _, config := range esoutput.ServiceConfigurations {
+		// check through gateway load balancer attachments and select the service for nuking
+		for _, gwlb := range config.GatewayLoadBalancerArns {
+			if slices.Contains(attachedLoadBalancers, awsgo.StringValue(gwlb)) {
+				nukableEndpointServices[config.ServiceId] = struct{}{}
+			}
+		}
+
+		// check through network load balancer attachments and select the service for nuking
+		for _, nwlb := range config.NetworkLoadBalancerArns {
+			if slices.Contains(attachedLoadBalancers, awsgo.StringValue(nwlb)) {
+				nukableEndpointServices[config.ServiceId] = struct{}{}
+			}
+		}
+	}
+
+	logging.Debug(fmt.Sprintf("Found %d Endpoint services attached with the load balancers to nuke.", len(nukableEndpointServices)))
+
+	// nuke the endpoint services
+	for endpointService := range nukableEndpointServices {
+		_, err := client.DeleteVpcEndpointServiceConfigurations(&ec2.DeleteVpcEndpointServiceConfigurationsInput{
+			ServiceIds: []*string{endpointService},
+		})
+		if err != nil {
+			logging.Debug(fmt.Sprintf("Failed to delete endpoint service %v for %s", awsgo.StringValue(endpointService), vpcID))
+			return errors.WithStackTrace(err)
+		}
+	}
+	logging.Debug(fmt.Sprintf("Successfully deleted he endpoints attached with load balancers for %s", vpcID))
+
+	// nuke the load-balancers
+	for _, lb := range attachedLoadBalancers {
+		_, err := elbclient.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{
+			LoadBalancerArn: awsgo.String(lb),
+		})
+		if err != nil {
+			logging.Debug(fmt.Sprintf("Failed to delete loadbalancer %v for %s", lb, vpcID))
+			return errors.WithStackTrace(err)
+		}
+	}
+
+	logging.Debug(fmt.Sprintf("Successfully deleted loadbalancers attached %s", vpcID))
+	return nil
+}
+
+func nukeTargetGroups(client elbv2iface.ELBV2API, vpcID string) error {
+	logging.Debug(fmt.Sprintf("Describing target groups for %s", vpcID))
+
+	output, err := client.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{})
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe target groups for %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+
+	for _, tg := range output.TargetGroups {
+		// if the target group is not for this vpc, then skip
+		if tg.VpcId != nil && awsgo.StringValue(tg.VpcId) == vpcID {
+			_, err := client.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{
+				TargetGroupArn: tg.TargetGroupArn,
+			})
+			if err != nil {
+				logging.Debug(fmt.Sprintf("Failed to delete target group %v for %s", *tg.TargetGroupArn, vpcID))
+				return errors.WithStackTrace(err)
+			}
+		}
+
+	}
+
+	logging.Debug(fmt.Sprintf("Successfully deleted target group attached %s", vpcID))
+
+	return nil
+}
+
+func nukeEc2Instances(client ec2iface.EC2API, vpcID string) error {
+	logging.Debug(fmt.Sprintf("Describing instances for %s", vpcID))
+	output, err := client.DescribeInstances(&ec2.DescribeInstancesInput{
+		Filters: []*ec2.Filter{
+			{
+				Name: awsgo.String("network-interface.vpc-id"),
+				Values: awsgo.StringSlice([]string{
+					vpcID,
+				}),
+			},
+		},
+	})
+
+	if err != nil {
+		logging.Debug(fmt.Sprintf("Failed to describe instances for %s", vpcID))
+		return errors.WithStackTrace(err)
+	}
+
+	var terminateInstances []*string
+	for _, instance := range output.Reservations {
+		for _, i := range instance.Instances {
+			terminateInstances = append(terminateInstances, i.InstanceId)
+		}
+	}
+
+	if len(terminateInstances) > 0 {
+		logging.Debug(fmt.Sprintf("Found %d VPC attached instances to Nuke.", len(terminateInstances)))
+
+		_, err := client.TerminateInstances(&ec2.TerminateInstancesInput{
+			InstanceIds: terminateInstances,
+		})
+		if err != nil {
+			logging.Debug(fmt.Sprintf("Failed to terminate instances for %s", vpcID))
+			return errors.WithStackTrace(err)
+		}
+
+		// weight for terminate the instances
+		logging.Debug(fmt.Sprintf("waiting for the instance to be terminated for %s", vpcID))
+		err = client.WaitUntilInstanceTerminated(&ec2.DescribeInstancesInput{
+			InstanceIds: terminateInstances,
+		})
+		if err != nil {
+			logging.Debug(fmt.Sprintf("Failed to wait instance termination for %s", vpcID))
+			return errors.WithStackTrace(err)
+		}
+	}
+
+	logging.Debug(fmt.Sprintf("Successfully deleted instances for %s", vpcID))
+	return nil
+}
diff --git a/aws/resources/ec2_vpc_types.go b/aws/resources/ec2_vpc_types.go
index 4f3105e..d934e40 100644
--- a/aws/resources/ec2_vpc_types.go
+++ b/aws/resources/ec2_vpc_types.go
@@ -7,19 +7,23 @@ import (
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/aws/aws-sdk-go/service/elbv2"
+	"github.com/aws/aws-sdk-go/service/elbv2/elbv2iface"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
 type EC2VPCs struct {
 	BaseAwsResource
-	Client ec2iface.EC2API
-	Region string
-	VPCIds []string
+	Client    ec2iface.EC2API
+	ELBClient elbv2iface.ELBV2API
+	Region    string
+	VPCIds    []string
 }
 
 func (v *EC2VPCs) Init(session *session.Session) {
 	v.Client = ec2.New(session)
+	v.ELBClient = elbv2.New(session)
 }
 
 // ResourceName - the simple name of the aws resource
diff --git a/aws/resources/network_firewall.go b/aws/resources/network_firewall.go
new file mode 100644
index 0000000..0556096
--- /dev/null
+++ b/aws/resources/network_firewall.go
@@ -0,0 +1,162 @@
+package resources
+
+import (
+	"context"
+	"slices"
+	"time"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (nfw *NetworkFirewall) setFirstSeenTag(resource *networkfirewall.Firewall, value time.Time) error {
+	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
+		ResourceArn: resource.FirewallArn,
+		Tags: []*networkfirewall.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	return errors.WithStackTrace(err)
+}
+
+func (nfw *NetworkFirewall) getFirstSeenTag(resource *networkfirewall.Firewall) (*time.Time, error) {
+	for _, tag := range resource.Tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeNetworkFirewall(firewall *networkfirewall.Firewall, firstSeenTime *time.Time, configObj config.Config) bool {
+	var identifierName string
+	tags := util.ConvertNetworkFirewallTagsToMap(firewall.Tags)
+
+	identifierName = awsgo.StringValue(firewall.FirewallName) // set the default
+	if v, ok := tags["Name"]; ok {
+		identifierName = v
+	}
+
+	return configObj.NetworkFirewall.ShouldInclude(config.ResourceValue{
+		Name: &identifierName,
+		Tags: tags,
+		Time: firstSeenTime,
+	})
+}
+
+func (nfw *NetworkFirewall) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+	var identifiers []*string
+
+	metaOutput, err := nfw.Client.ListFirewalls(nil)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	var deleteprotected []string
+	// describe the firewalls to get more info
+	for _, firewall := range metaOutput.Firewalls {
+		output, err := nfw.Client.DescribeFirewall(&networkfirewall.DescribeFirewallInput{
+			FirewallArn: firewall.FirewallArn,
+		})
+		if err != nil {
+			logging.Errorf("[Failed] to describe the firewall %s", awsgo.StringValue(firewall.FirewallArn))
+			return nil, errors.WithStackTrace(err)
+		}
+
+		if output.Firewall == nil {
+			logging.Errorf("[Failed] no firewall information found for %s", awsgo.StringValue(firewall.FirewallArn))
+			continue
+		}
+
+		// check first seen tag
+		firstSeenTime, err := nfw.getFirstSeenTag(output.Firewall)
+		if err != nil {
+			logging.Errorf(
+				"Unable to retrieve tags for Firewall: %s, with error: %s", awsgo.StringValue(firewall.FirewallName), err)
+			continue
+		}
+
+		// if the first seen tag is not there, then create one
+		if firstSeenTime == nil {
+			now := time.Now().UTC()
+			firstSeenTime = &now
+			if err := nfw.setFirstSeenTag(output.Firewall, time.Now().UTC()); err != nil {
+				logging.Errorf(
+					"Unable to apply first seen tag Firewall: %s, with error: %s", awsgo.StringValue(firewall.FirewallName), err)
+				continue
+			}
+		}
+
+		// check the resource is delete protected
+		if awsgo.BoolValue(output.Firewall.DeleteProtection) {
+			deleteprotected = append(deleteprotected, awsgo.StringValue(firewall.FirewallName))
+		}
+
+		if shouldIncludeNetworkFirewall(output.Firewall, firstSeenTime, configObj) {
+			identifiers = append(identifiers, firewall.FirewallName)
+		}
+	}
+
+	nfw.VerifyNukablePermissions(identifiers, func(id *string) error {
+		// check the resource is enabled delete protection
+		if slices.Contains(deleteprotected, awsgo.StringValue(id)) {
+			return util.ErrDeleteProtectionEnabled
+		}
+		return nil
+	})
+
+	return identifiers, nil
+}
+
+func (nfw *NetworkFirewall) nukeAll(identifiers []*string) error {
+	if len(identifiers) == 0 {
+		logging.Debugf("No Network Firewalls to nuke in region %s", nfw.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting Network firewalls in region %s", nfw.Region)
+	var deleted []*string
+
+	for _, id := range identifiers {
+		if nukable, err := nfw.IsNukable(*id); !nukable {
+			logging.Debugf("[Skipping] %s nuke because %v", *id, err)
+			continue
+		}
+
+		_, err := nfw.Client.DeleteFirewall(&networkfirewall.DeleteFirewallInput{
+			FirewallName: id,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "Network Firewall",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deleted = append(deleted, id)
+		}
+	}
+
+	logging.Debugf("[OK] %d Network Firewall(s) deleted in %s", len(deleted), nfw.Region)
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_policy.go b/aws/resources/network_firewall_policy.go
new file mode 100644
index 0000000..4ed3938
--- /dev/null
+++ b/aws/resources/network_firewall_policy.go
@@ -0,0 +1,146 @@
+package resources
+
+import (
+	"context"
+	"time"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (nfw *NetworkFirewallPolicy) setFirstSeenTag(resource *networkfirewall.FirewallPolicyResponse, value time.Time) error {
+	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
+		ResourceArn: resource.FirewallPolicyArn,
+		Tags: []*networkfirewall.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	return errors.WithStackTrace(err)
+}
+
+func (nfw *NetworkFirewallPolicy) getFirstSeenTag(resource *networkfirewall.FirewallPolicyResponse) (*time.Time, error) {
+	for _, tag := range resource.Tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeNetworkFirewallPolicy(firewall *networkfirewall.FirewallPolicyResponse, firstSeenTime *time.Time, configObj config.Config) bool {
+	// if the firewall policy has any attachments, then we can't remove that policy
+	if awsgo.Int64Value(firewall.NumberOfAssociations) > 0 {
+		logging.Debugf("[Skipping] the policy %s is still in use", awsgo.StringValue(firewall.FirewallPolicyName))
+		return false
+	}
+
+	var identifierName string
+	tags := util.ConvertNetworkFirewallTagsToMap(firewall.Tags)
+
+	if v, ok := tags["Name"]; ok {
+		identifierName = v
+	}
+	return configObj.NetworkFirewallPolicy.ShouldInclude(config.ResourceValue{
+		Name: &identifierName,
+		Tags: tags,
+		Time: firstSeenTime,
+	})
+}
+
+func (nfw *NetworkFirewallPolicy) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+	var identifiers []*string
+
+	metaOutput, err := nfw.Client.ListFirewallPolicies(nil)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	for _, policy := range metaOutput.FirewallPolicies {
+
+		output, err := nfw.Client.DescribeFirewallPolicy(&networkfirewall.DescribeFirewallPolicyInput{
+			FirewallPolicyArn: policy.Arn,
+		})
+		if err != nil {
+			logging.Errorf("[Failed] to describe the firewall policy %s", awsgo.StringValue(policy.Name))
+			return nil, errors.WithStackTrace(err)
+		}
+
+		if output.FirewallPolicyResponse == nil {
+			logging.Errorf("[Failed] no firewall policy information found for %s", awsgo.StringValue(policy.Name))
+			continue
+		}
+
+		// check first seen tag
+		firstSeenTime, err := nfw.getFirstSeenTag(output.FirewallPolicyResponse)
+		if err != nil {
+			logging.Errorf(
+				"Unable to retrieve tags for Rule group: %s, with error: %s", awsgo.StringValue(policy.Name), err)
+			continue
+		}
+
+		// if the first seen tag is not there, then create one
+		if firstSeenTime == nil {
+			now := time.Now().UTC()
+			firstSeenTime = &now
+			if err := nfw.setFirstSeenTag(output.FirewallPolicyResponse, time.Now().UTC()); err != nil {
+				logging.Errorf(
+					"Unable to apply first seen tag Rule group: %s, with error: %s", awsgo.StringValue(policy.Name), err)
+				continue
+			}
+		}
+
+		if shouldIncludeNetworkFirewallPolicy(output.FirewallPolicyResponse, firstSeenTime, configObj) {
+			identifiers = append(identifiers, policy.Name)
+		}
+	}
+
+	return identifiers, nil
+}
+
+func (nfw *NetworkFirewallPolicy) nukeAll(identifiers []*string) error {
+	if len(identifiers) == 0 {
+		logging.Debugf("No Network Firewall policy to nuke in region %s", nfw.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting Network firewall policy in region %s", nfw.Region)
+	var deleted []*string
+
+	for _, id := range identifiers {
+		_, err := nfw.Client.DeleteFirewallPolicy(&networkfirewall.DeleteFirewallPolicyInput{
+			FirewallPolicyName: id,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "Network Firewall policy",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deleted = append(deleted, id)
+		}
+	}
+
+	logging.Debugf("[OK] %d Network Policy(s) deleted in %s", len(deleted), nfw.Region)
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_policy_types.go b/aws/resources/network_firewall_policy_types.go
new file mode 100644
index 0000000..93c9a1d
--- /dev/null
+++ b/aws/resources/network_firewall_policy_types.go
@@ -0,0 +1,59 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+type NetworkFirewallPolicy struct {
+	BaseAwsResource
+	Client      networkfirewalliface.NetworkFirewallAPI
+	Region      string
+	Identifiers []string
+}
+
+func (nfw *NetworkFirewallPolicy) Init(session *session.Session) {
+	nfw.BaseAwsResource.Init(session)
+	nfw.Client = networkfirewall.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (nfw *NetworkFirewallPolicy) ResourceName() string {
+	return "network-firewall-policy"
+}
+
+func (nfw *NetworkFirewallPolicy) ResourceIdentifiers() []string {
+	return nfw.Identifiers
+}
+
+func (nfw *NetworkFirewallPolicy) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle. Note that nat gateway does not support bulk delete, so
+	// we will be deleting this many in parallel using go routines. We conservatively pick 10 here, both to limit
+	// overloading the runtime and to avoid AWS throttling with many API calls.
+	return 10
+}
+
+func (nfw *NetworkFirewallPolicy) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := nfw.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	nfw.Identifiers = awsgo.StringValueSlice(identifiers)
+	return nfw.Identifiers, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (nfw *NetworkFirewallPolicy) Nuke(identifiers []string) error {
+	if err := nfw.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_resource_policy.go b/aws/resources/network_firewall_resource_policy.go
new file mode 100644
index 0000000..57f28af
--- /dev/null
+++ b/aws/resources/network_firewall_resource_policy.go
@@ -0,0 +1,97 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// The Network Firewall service supports only one type of resource-based policy called a resource policy, which is attached to a shared firewall policy or rule group
+// References :
+// - https://docs.aws.amazon.com/network-firewall/latest/developerguide/security_iam_resource-based-policy-examples.html
+// - https://docs.aws.amazon.com/network-firewall/latest/developerguide/sharing.html
+// - https://docs.aws.amazon.com/ram/latest/userguide/what-is.html
+func (nfw *NetworkFirewallResourcePolicy) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+	var identifiers []*string
+
+	var resourceArns []*string
+	// list the firewall policies and rule group
+
+	{
+		policyMeta, err := nfw.Client.ListFirewallPolicies(nil)
+		if err != nil {
+			return nil, errors.WithStackTrace(err)
+		}
+
+		for _, policy := range policyMeta.FirewallPolicies {
+			resourceArns = append(resourceArns, policy.Arn)
+		}
+		groupMeta, err := nfw.Client.ListRuleGroups(nil)
+		if err != nil {
+			return nil, errors.WithStackTrace(err)
+		}
+		for _, group := range groupMeta.RuleGroups {
+			resourceArns = append(resourceArns, group.Arn)
+		}
+	}
+
+	// get the resource policies attached on these arns
+	for _, arn := range resourceArns {
+		output, err := nfw.Client.DescribeResourcePolicy(&networkfirewall.DescribeResourcePolicyInput{
+			ResourceArn: arn,
+		})
+		if err != nil && util.TransformAWSError(err) != util.ErrResourceNotFoundException {
+			return nil, errors.WithStackTrace(err)
+		}
+
+		// if the policy exists for a resource
+		if output.Policy != nil {
+			if configObj.NetworkFirewallResourcePolicy.ShouldInclude(config.ResourceValue{
+				Name: arn,
+			}) {
+				identifiers = append(identifiers, arn)
+			}
+		}
+	}
+	return identifiers, nil
+}
+
+func (nfw *NetworkFirewallResourcePolicy) nukeAll(identifiers []*string) error {
+	if len(identifiers) == 0 {
+		logging.Debugf("No Network Firewall resource policy to nuke in region %s", nfw.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting Network firewall resource policy in region %s", nfw.Region)
+	var deleted []*string
+
+	for _, id := range identifiers {
+		_, err := nfw.Client.DeleteResourcePolicy(&networkfirewall.DeleteResourcePolicyInput{
+			ResourceArn: id,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "Network Firewall Resource policy",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deleted = append(deleted, id)
+		}
+	}
+
+	logging.Debugf("[OK] %d Network Resource Policy(s) deleted in %s", len(deleted), nfw.Region)
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_resource_policy_types.go b/aws/resources/network_firewall_resource_policy_types.go
new file mode 100644
index 0000000..557beac
--- /dev/null
+++ b/aws/resources/network_firewall_resource_policy_types.go
@@ -0,0 +1,58 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+type NetworkFirewallResourcePolicy struct {
+	BaseAwsResource
+	Client      networkfirewalliface.NetworkFirewallAPI
+	Region      string
+	Identifiers []string
+}
+
+func (nfrp *NetworkFirewallResourcePolicy) Init(session *session.Session) {
+	nfrp.Client = networkfirewall.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (nfrp *NetworkFirewallResourcePolicy) ResourceName() string {
+	return "network-firewall-resource-policy"
+}
+
+func (nfrp *NetworkFirewallResourcePolicy) ResourceIdentifiers() []string {
+	return nfrp.Identifiers
+}
+
+func (nfrp *NetworkFirewallResourcePolicy) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle. Note that nat gateway does not support bulk delete, so
+	// we will be deleting this many in parallel using go routines. We conservatively pick 10 here, both to limit
+	// overloading the runtime and to avoid AWS throttling with many API calls.
+	return 10
+}
+
+func (nfrp *NetworkFirewallResourcePolicy) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := nfrp.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	nfrp.Identifiers = awsgo.StringValueSlice(identifiers)
+	return nfrp.Identifiers, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (nfrp *NetworkFirewallResourcePolicy) Nuke(identifiers []string) error {
+	if err := nfrp.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_rule_group.go b/aws/resources/network_firewall_rule_group.go
new file mode 100644
index 0000000..670fb56
--- /dev/null
+++ b/aws/resources/network_firewall_rule_group.go
@@ -0,0 +1,163 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (nfw *NetworkFirewallRuleGroup) setFirstSeenTag(resource *networkfirewall.RuleGroupResponse, value time.Time) error {
+	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
+		ResourceArn: resource.RuleGroupArn,
+		Tags: []*networkfirewall.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	return errors.WithStackTrace(err)
+}
+
+func (nfw *NetworkFirewallRuleGroup) getFirstSeenTag(resource *networkfirewall.RuleGroupResponse) (*time.Time, error) {
+	for _, tag := range resource.Tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeNetworkFirewallRuleGroup(group *networkfirewall.RuleGroupResponse, firstSeenTime *time.Time, configObj config.Config) bool {
+	// if the firewall policy has any attachments, then we can't remove that policy
+	if awsgo.Int64Value(group.NumberOfAssociations) > 0 {
+		logging.Debugf("[Skipping] the rule group %s is still in use", awsgo.StringValue(group.RuleGroupName))
+		return false
+	}
+
+	var identifierName string
+	tags := util.ConvertNetworkFirewallTagsToMap(group.Tags)
+
+	identifierName = awsgo.StringValue(group.RuleGroupName) // set the default
+	if v, ok := tags["Name"]; ok {
+		identifierName = v
+	}
+
+	return configObj.NetworkFirewallRuleGroup.ShouldInclude(config.ResourceValue{
+		Name: &identifierName,
+		Tags: tags,
+		Time: firstSeenTime,
+	})
+}
+
+func (nfw *NetworkFirewallRuleGroup) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+	var identifiers []*string
+
+	meta, err := nfw.Client.ListRuleGroups(nil)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	for _, group := range meta.RuleGroups {
+		output, err := nfw.Client.DescribeRuleGroup(&networkfirewall.DescribeRuleGroupInput{
+			RuleGroupArn: group.Arn,
+		})
+		if err != nil {
+			logging.Errorf("[Failed] to describe the firewall rule group %s", awsgo.StringValue(group.Name))
+			return nil, errors.WithStackTrace(err)
+		}
+
+		if output.RuleGroupResponse == nil {
+			logging.Errorf("[Failed] no firewall rule group information found for %s", awsgo.StringValue(group.Name))
+			continue
+		}
+
+		// check first seen tag
+		firstSeenTime, err := nfw.getFirstSeenTag(output.RuleGroupResponse)
+		if err != nil {
+			logging.Errorf(
+				"Unable to retrieve tags for Rule group: %s, with error: %s", awsgo.StringValue(group.Name), err)
+			continue
+		}
+
+		// if the first seen tag is not there, then create one
+		if firstSeenTime == nil {
+			now := time.Now().UTC()
+			firstSeenTime = &now
+			if err := nfw.setFirstSeenTag(output.RuleGroupResponse, time.Now().UTC()); err != nil {
+				logging.Errorf(
+					"Unable to apply first seen tag Rule group: %s, with error: %s", awsgo.StringValue(group.Name), err)
+				continue
+			}
+		}
+
+		if shouldIncludeNetworkFirewallRuleGroup(output.RuleGroupResponse, firstSeenTime, configObj) {
+			identifiers = append(identifiers, group.Name)
+
+			raw := awsgo.StringValue(group.Name)
+			nfw.RuleGroups[raw] = RuleGroup{
+				Name: output.RuleGroupResponse.RuleGroupName,
+				Type: output.RuleGroupResponse.Type,
+			}
+		}
+	}
+
+	return identifiers, nil
+}
+
+func (nfw *NetworkFirewallRuleGroup) nukeAll(identifiers []*string) error {
+	if len(identifiers) == 0 {
+		logging.Debugf("No Network Firewall rule group to nuke in region %s", nfw.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting Network firewall rule group in region %s", nfw.Region)
+	var deleted []*string
+
+	for _, id := range identifiers {
+		// check and get the type for this identifier
+		group, ok := nfw.RuleGroups[awsgo.StringValue(id)]
+		if !ok {
+			logging.Errorf("couldn't find the rule group type for %s", awsgo.StringValue(id))
+			return fmt.Errorf("couldn't find the rule group type for %s", awsgo.StringValue(id))
+		}
+
+		// delete the rule group
+		_, err := nfw.Client.DeleteRuleGroup(&networkfirewall.DeleteRuleGroupInput{
+			RuleGroupName: id,
+			Type:          group.Type,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "Network Firewall Rule group",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deleted = append(deleted, id)
+		}
+	}
+
+	logging.Debugf("[OK] %d Network Firewall Rule group(s) deleted in %s", len(deleted), nfw.Region)
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_rule_group_types.go b/aws/resources/network_firewall_rule_group_types.go
new file mode 100644
index 0000000..693e945
--- /dev/null
+++ b/aws/resources/network_firewall_rule_group_types.go
@@ -0,0 +1,67 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+type RuleGroup struct {
+	Name, Type *string
+}
+
+type NetworkFirewallRuleGroup struct {
+	BaseAwsResource
+	Client      networkfirewalliface.NetworkFirewallAPI
+	Region      string
+	Identifiers []string
+	// Note: It is mandatory to pass the rule type while nuking it.
+	// This map can be used to store information about a rule group name as key and value.
+	// When invoking the nuke method, information about a rule can be easily retrieved without making another API request.
+	RuleGroups map[string]RuleGroup
+}
+
+func (nfrg *NetworkFirewallRuleGroup) Init(session *session.Session) {
+	nfrg.Client = networkfirewall.New(session)
+	nfrg.RuleGroups = make(map[string]RuleGroup, 0)
+}
+
+// ResourceName - the simple name of the aws resource
+func (nfrg *NetworkFirewallRuleGroup) ResourceName() string {
+	return "network-firewall-rule-group"
+}
+
+func (nfrg *NetworkFirewallRuleGroup) ResourceIdentifiers() []string {
+	return nfrg.Identifiers
+}
+
+func (nfrg *NetworkFirewallRuleGroup) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle. Note that nat gateway does not support bulk delete, so
+	// we will be deleting this many in parallel using go routines. We conservatively pick 10 here, both to limit
+	// overloading the runtime and to avoid AWS throttling with many API calls.
+	return 10
+}
+
+func (nfrg *NetworkFirewallRuleGroup) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := nfrg.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	nfrg.Identifiers = awsgo.StringValueSlice(identifiers)
+	return nfrg.Identifiers, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (nfrg *NetworkFirewallRuleGroup) Nuke(identifiers []string) error {
+	if err := nfrg.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_tls_config.go b/aws/resources/network_firewall_tls_config.go
new file mode 100644
index 0000000..cc3059d
--- /dev/null
+++ b/aws/resources/network_firewall_tls_config.go
@@ -0,0 +1,141 @@
+package resources
+
+import (
+	"context"
+	"time"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (nfw *NetworkFirewallTLSConfig) setFirstSeenTag(resource *networkfirewall.TLSInspectionConfigurationResponse, value time.Time) error {
+	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
+		ResourceArn: resource.TLSInspectionConfigurationArn,
+		Tags: []*networkfirewall.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	return errors.WithStackTrace(err)
+}
+
+func (nfw *NetworkFirewallTLSConfig) getFirstSeenTag(resource *networkfirewall.TLSInspectionConfigurationResponse) (*time.Time, error) {
+	for _, tag := range resource.Tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeNetworkFirewallTLSConfig(tlsconfig *networkfirewall.TLSInspectionConfigurationResponse, firstSeenTime *time.Time, configObj config.Config) bool {
+
+	var identifierName string
+	tags := util.ConvertNetworkFirewallTagsToMap(tlsconfig.Tags)
+
+	identifierName = awsgo.StringValue(tlsconfig.TLSInspectionConfigurationName) // set the default
+	if v, ok := tags["Name"]; ok {
+		identifierName = v
+	}
+
+	return configObj.NetworkFirewallTLSConfig.ShouldInclude(config.ResourceValue{
+		Name: &identifierName,
+		Tags: tags,
+		Time: firstSeenTime,
+	})
+}
+
+func (nftc *NetworkFirewallTLSConfig) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+	var identifiers []*string
+	meta, err := nftc.Client.ListTLSInspectionConfigurations(nil)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	for _, tlsconfig := range meta.TLSInspectionConfigurations {
+		output, err := nftc.Client.DescribeTLSInspectionConfiguration(&networkfirewall.DescribeTLSInspectionConfigurationInput{
+			TLSInspectionConfigurationArn: tlsconfig.Arn,
+		})
+		if err != nil {
+			logging.Errorf("[Failed] to describe the firewall TLS inspection configuation %s", awsgo.StringValue(tlsconfig.Name))
+			return nil, errors.WithStackTrace(err)
+		}
+
+		if output.TLSInspectionConfigurationResponse == nil {
+			logging.Errorf("[Failed] no firewall TLS inspection configuation information found for %s", awsgo.StringValue(tlsconfig.Name))
+			continue
+		}
+
+		// check first seen tag
+		firstSeenTime, err := nftc.getFirstSeenTag(output.TLSInspectionConfigurationResponse)
+		if err != nil {
+			logging.Errorf(
+				"Unable to retrieve tags for TLS inspection configurations: %s, with error: %s", awsgo.StringValue(tlsconfig.Name), err)
+			continue
+		}
+
+		// if the first seen tag is not there, then create one
+		if firstSeenTime == nil {
+			now := time.Now().UTC()
+			firstSeenTime = &now
+			if err := nftc.setFirstSeenTag(output.TLSInspectionConfigurationResponse, time.Now().UTC()); err != nil {
+				logging.Errorf(
+					"Unable to apply first seen tag TLS inspection configurations: %s, with error: %s", awsgo.StringValue(tlsconfig.Name), err)
+				continue
+			}
+		}
+
+		if shouldIncludeNetworkFirewallTLSConfig(output.TLSInspectionConfigurationResponse, firstSeenTime, configObj) {
+			identifiers = append(identifiers, tlsconfig.Name)
+		}
+	}
+
+	return identifiers, nil
+}
+
+func (nftc *NetworkFirewallTLSConfig) nukeAll(identifiers []*string) error {
+	if len(identifiers) == 0 {
+		logging.Debugf("No Network Firewall TLS inspection configurations to nuke in region %s", nftc.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting Network firewall TLS inspection configurations in region %s", nftc.Region)
+	var deleted []*string
+
+	for _, id := range identifiers {
+		_, err := nftc.Client.DeleteTLSInspectionConfiguration(&networkfirewall.DeleteTLSInspectionConfigurationInput{
+			TLSInspectionConfigurationName: id,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "Network Firewall TLS inspection configurations",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deleted = append(deleted, id)
+		}
+	}
+
+	logging.Debugf("[OK] %d Network Firewall TLS inspection configurations(s) deleted in %s", len(deleted), nftc.Region)
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_tls_config_types.go b/aws/resources/network_firewall_tls_config_types.go
new file mode 100644
index 0000000..921194b
--- /dev/null
+++ b/aws/resources/network_firewall_tls_config_types.go
@@ -0,0 +1,58 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+type NetworkFirewallTLSConfig struct {
+	BaseAwsResource
+	Client      networkfirewalliface.NetworkFirewallAPI
+	Region      string
+	Identifiers []string
+}
+
+func (nftc *NetworkFirewallTLSConfig) Init(session *session.Session) {
+	nftc.Client = networkfirewall.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (nftc *NetworkFirewallTLSConfig) ResourceName() string {
+	return "network-firewall-tls-config"
+}
+
+func (nftc *NetworkFirewallTLSConfig) ResourceIdentifiers() []string {
+	return nftc.Identifiers
+}
+
+func (nftc *NetworkFirewallTLSConfig) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle. Note that nat gateway does not support bulk delete, so
+	// we will be deleting this many in parallel using go routines. We conservatively pick 10 here, both to limit
+	// overloading the runtime and to avoid AWS throttling with many API calls.
+	return 10
+}
+
+func (nftc *NetworkFirewallTLSConfig) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := nftc.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	nftc.Identifiers = awsgo.StringValueSlice(identifiers)
+	return nftc.Identifiers, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (nftc *NetworkFirewallTLSConfig) Nuke(identifiers []string) error {
+	if err := nftc.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/network_firewall_types.go b/aws/resources/network_firewall_types.go
new file mode 100644
index 0000000..d6f3afb
--- /dev/null
+++ b/aws/resources/network_firewall_types.go
@@ -0,0 +1,59 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+type NetworkFirewall struct {
+	BaseAwsResource
+	Client      networkfirewalliface.NetworkFirewallAPI
+	Region      string
+	Identifiers []string
+}
+
+func (nfw *NetworkFirewall) Init(session *session.Session) {
+	nfw.BaseAwsResource.Init(session)
+	nfw.Client = networkfirewall.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (nfw *NetworkFirewall) ResourceName() string {
+	return "network-firewall"
+}
+
+func (nfw *NetworkFirewall) ResourceIdentifiers() []string {
+	return nfw.Identifiers
+}
+
+func (nfw *NetworkFirewall) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle. Note that nat gateway does not support bulk delete, so
+	// we will be deleting this many in parallel using go routines. We conservatively pick 10 here, both to limit
+	// overloading the runtime and to avoid AWS throttling with many API calls.
+	return 10
+}
+
+func (nfw *NetworkFirewall) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := nfw.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	nfw.Identifiers = awsgo.StringValueSlice(identifiers)
+	return nfw.Identifiers, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (nfw *NetworkFirewall) Nuke(identifiers []string) error {
+	if err := nfw.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/config/config.go b/config/config.go
index b8fc3c5..b20a05a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -105,6 +105,11 @@ type Config struct {
 	NetworkACL                      ResourceType               `yaml:"NetworkACL"`
 	NetworkInterface                ResourceType               `yaml:"NetworkInterface"`
 	SecurityGroup                   EC2ResourceType            `yaml:"SecurityGroup"`
+	NetworkFirewall                 ResourceType               `yaml:"NetworkFirewall"`
+	NetworkFirewallPolicy           ResourceType               `yaml:"NetworkFirewallPolicy"`
+	NetworkFirewallRuleGroup        ResourceType               `yaml:"NetworkFirewallRuleGroup"`
+	NetworkFirewallTLSConfig        ResourceType               `yaml:"NetworkFirewallTLSConfig"`
+	NetworkFirewallResourcePolicy   ResourceType               `yaml:"NetworkFirewallResourcePolicy"`
 }
 
 func (c *Config) addTimeAfterFilter(timeFilter *time.Time, fieldName string) {
diff --git a/util/error.go b/util/error.go
index d3b26a4..ec13d5d 100644
--- a/util/error.go
+++ b/util/error.go
@@ -13,6 +13,8 @@ var ErrDifferentOwner = errors.New("error:DIFFERENT_OWNER")
 var ErrContextExecutionTimeout = errors.New("error:EXECUTION_TIMEOUT")
 var ErrInterfaceIDNotFound = errors.New("error:InterfaceIdNotFound")
 var ErrInvalidPermisionNotFound = errors.New("error:InvalidPermission.NotFound")
+var ErrDeleteProtectionEnabled = errors.New("error:DeleteProtectionEnabled")
+var ErrResourceNotFoundException = errors.New("error:ErrResourceNotFoundException")
 
 const AWsUnauthorizedError string = "UnauthorizedOperation"
 const AwsDryRunSuccess string = "Request would have succeeded, but DryRun flag is set."
@@ -40,6 +42,10 @@ func TransformAWSError(err error) error {
 	if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidPermission.NotFound" {
 		return ErrInvalidPermisionNotFound
 	}
+	if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceNotFoundException" {
+		return ErrResourceNotFoundException
+	}
+
 	return err
 
 }
diff --git a/util/tag.go b/util/tag.go
index 3ab9960..0e4c4dd 100644
--- a/util/tag.go
+++ b/util/tag.go
@@ -4,6 +4,7 @@ import (
 	"github.com/aws/aws-sdk-go/service/autoscaling"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/aws/aws-sdk-go/service/iam"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
 	"github.com/aws/aws-sdk-go/service/rds"
 	"github.com/aws/aws-sdk-go/service/s3"
 )
@@ -70,3 +71,12 @@ func GetEC2ResourceNameTagValue(tags []*ec2.Tag) *string {
 	}
 	return nil
 }
+
+func ConvertNetworkFirewallTagsToMap(tags []*networkfirewall.Tag) map[string]string {
+	tagMap := make(map[string]string)
+	for _, tag := range tags {
+		tagMap[*tag.Key] = *tag.Value
+	}
+
+	return tagMap
+}


Test patch:
diff --git a/aws/resources/ec2_vpc_test.go b/aws/resources/ec2_vpc_test.go
index cbee4bf..9e1e2c7 100644
--- a/aws/resources/ec2_vpc_test.go
+++ b/aws/resources/ec2_vpc_test.go
@@ -6,10 +6,12 @@ import (
 	"testing"
 	"time"
 
-	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go/aws"
 	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/aws/aws-sdk-go/service/elbv2"
+	"github.com/aws/aws-sdk-go/service/elbv2/elbv2iface"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/stretchr/testify/require"
@@ -17,8 +19,13 @@ import (
 
 type mockedEC2VPCs struct {
 	ec2iface.EC2API
-	DescribeVpcsOutput ec2.DescribeVpcsOutput
-	DeleteVpcOutput    ec2.DeleteVpcOutput
+	DescribeVpcsOutput                             ec2.DescribeVpcsOutput
+	DeleteVpcOutput                                ec2.DeleteVpcOutput
+	DescribeVpcPeeringConnectionsOutput            ec2.DescribeVpcPeeringConnectionsOutput
+	DescribeInstancesOutput                        ec2.DescribeInstancesOutput
+	TerminateInstancesOutput                       ec2.TerminateInstancesOutput
+	DescribeVpcEndpointServiceConfigurationsOutput ec2.DescribeVpcEndpointServiceConfigurationsOutput
+	DeleteVpcEndpointServiceConfigurationsOutput   ec2.DeleteVpcEndpointServiceConfigurationsOutput
 }
 
 func (m mockedEC2VPCs) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
@@ -28,7 +35,27 @@ func (m mockedEC2VPCs) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.Describe
 func (m mockedEC2VPCs) DeleteVpc(input *ec2.DeleteVpcInput) (*ec2.DeleteVpcOutput, error) {
 	return &m.DeleteVpcOutput, nil
 }
+func (m mockedEC2VPCs) DescribeVpcPeeringConnectionsPages(input *ec2.DescribeVpcPeeringConnectionsInput, callback func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool) error {
+	callback(&m.DescribeVpcPeeringConnectionsOutput, true)
+	return nil
+}
+func (m mockedEC2VPCs) DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
+	return &m.DescribeInstancesOutput, nil
+}
+
+func (m mockedEC2VPCs) TerminateInstances(*ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) {
+	return &m.TerminateInstancesOutput, nil
+}
+func (m mockedEC2VPCs) WaitUntilInstanceTerminated(*ec2.DescribeInstancesInput) error {
+	return nil
+}
 
+func (m mockedEC2VPCs) DescribeVpcEndpointServiceConfigurations(*ec2.DescribeVpcEndpointServiceConfigurationsInput) (*ec2.DescribeVpcEndpointServiceConfigurationsOutput, error) {
+	return &m.DescribeVpcEndpointServiceConfigurationsOutput, nil
+}
+func (m mockedEC2VPCs) DeleteVpcEndpointServiceConfigurations(*ec2.DeleteVpcEndpointServiceConfigurationsInput) (*ec2.DeleteVpcEndpointServiceConfigurationsOutput, error) {
+	return &m.DeleteVpcEndpointServiceConfigurationsOutput, nil
+}
 func TestEC2VPC_GetAll(t *testing.T) {
 
 	t.Parallel()
@@ -128,3 +155,115 @@ func TestEC2VPC_GetAll(t *testing.T) {
 //	err := vpc.nukeAll([]string{"test-vpc-id1", "test-vpc-id2"})
 //	require.NoError(t, err)
 //}
+
+func TestEC2VPCPeeringConnections_NukeAll(t *testing.T) {
+	t.Parallel()
+	vpc := EC2VPCs{
+		Client: mockedEC2VPCs{
+			DescribeVpcPeeringConnectionsOutput: ec2.DescribeVpcPeeringConnectionsOutput{},
+		},
+	}
+
+	err := nukePeeringConnections(vpc.Client, "vpc-test-00001")
+	require.NoError(t, err)
+}
+
+type mockedEC2ELB struct {
+	elbv2iface.ELBV2API
+	DescribeLoadBalancersOutput elbv2.DescribeLoadBalancersOutput
+	DeleteLoadBalancerOutput    elbv2.DeleteLoadBalancerOutput
+
+	DescribeTargetGroupsOutput elbv2.DescribeTargetGroupsOutput
+	DeleteTargetGroupOutput    elbv2.DeleteTargetGroupOutput
+}
+
+func (m mockedEC2ELB) DescribeLoadBalancers(*elbv2.DescribeLoadBalancersInput) (*elbv2.DescribeLoadBalancersOutput, error) {
+	return &m.DescribeLoadBalancersOutput, nil
+}
+func (m mockedEC2ELB) DescribeTargetGroups(*elbv2.DescribeTargetGroupsInput) (*elbv2.DescribeTargetGroupsOutput, error) {
+	return &m.DescribeTargetGroupsOutput, nil
+}
+
+func (m mockedEC2ELB) DeleteLoadBalancer(*elbv2.DeleteLoadBalancerInput) (*elbv2.DeleteLoadBalancerOutput, error) {
+	return &m.DeleteLoadBalancerOutput, nil
+}
+func (m mockedEC2ELB) DeleteTargetGroup(*elbv2.DeleteTargetGroupInput) (*elbv2.DeleteTargetGroupOutput, error) {
+	return &m.DeleteTargetGroupOutput, nil
+}
+
+func TestAttachedLB_Nuke(t *testing.T) {
+	t.Parallel()
+	vpcID := "vpc-0e9a3e7c72d9f3a0f"
+
+	vpc := EC2VPCs{
+		Client: mockedEC2VPCs{
+			DescribeVpcEndpointServiceConfigurationsOutput: ec2.DescribeVpcEndpointServiceConfigurationsOutput{
+				ServiceConfigurations: []*ec2.ServiceConfiguration{
+					{
+						GatewayLoadBalancerArns: awsgo.StringSlice([]string{
+							"load-balancer-arn-00012",
+						}),
+					},
+				},
+			},
+		},
+		ELBClient: mockedEC2ELB{
+			DescribeLoadBalancersOutput: elbv2.DescribeLoadBalancersOutput{
+				LoadBalancers: []*elbv2.LoadBalancer{
+					{
+						VpcId:           awsgo.String(vpcID),
+						LoadBalancerArn: awsgo.String("load-balancer-arn-00012"),
+					},
+				},
+			},
+		},
+	}
+
+	err := nukeAttachedLB(vpc.Client, vpc.ELBClient, vpcID)
+	require.NoError(t, err)
+}
+
+func TestTargetGroup_Nuke(t *testing.T) {
+	t.Parallel()
+	vpcID := "vpc-0e9a3e7c72d9f3a0f"
+
+	vpc := EC2VPCs{
+		ELBClient: mockedEC2ELB{
+			DescribeTargetGroupsOutput: elbv2.DescribeTargetGroupsOutput{
+				TargetGroups: []*elbv2.TargetGroup{
+					{
+						VpcId:          awsgo.String(vpcID),
+						TargetGroupArn: awsgo.String("arn:aws:elasticloadbalancing:us-east-1:tg-001"),
+					},
+				},
+			},
+		},
+	}
+
+	err := nukeTargetGroups(vpc.ELBClient, vpcID)
+	require.NoError(t, err)
+}
+
+func TestEc2Instance_Nuke(t *testing.T) {
+	t.Parallel()
+	vpcID := "vpc-0e9a3e7c72d9f3a0f"
+
+	vpc := EC2VPCs{
+		Client: mockedEC2VPCs{
+			DescribeInstancesOutput: ec2.DescribeInstancesOutput{
+				Reservations: []*ec2.Reservation{
+					{
+						Instances: []*ec2.Instance{
+							{
+								InstanceId: awsgo.String("instance-001"),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	err := nukeEc2Instances(vpc.Client, vpcID)
+	require.NoError(t, err)
+}
diff --git a/aws/resources/network_firewall_policy_test.go b/aws/resources/network_firewall_policy_test.go
new file mode 100644
index 0000000..8858760
--- /dev/null
+++ b/aws/resources/network_firewall_policy_test.go
@@ -0,0 +1,158 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedNetworkFirewallPolicy struct {
+	networkfirewalliface.NetworkFirewallAPI
+	DeleteFirewallPolicyOutput   networkfirewall.DeleteFirewallPolicyOutput
+	ListFirewallPoliciesOutput   networkfirewall.ListFirewallPoliciesOutput
+	TagResourceOutput            networkfirewall.TagResourceOutput
+	DescribeFirewallPolicyOutput map[string]networkfirewall.DescribeFirewallPolicyOutput
+}
+
+func (m mockedNetworkFirewallPolicy) TagResource(*networkfirewall.TagResourceInput) (*networkfirewall.TagResourceOutput, error) {
+	return &m.TagResourceOutput, nil
+}
+
+func (m mockedNetworkFirewallPolicy) DeleteFirewallPolicy(*networkfirewall.DeleteFirewallPolicyInput) (*networkfirewall.DeleteFirewallPolicyOutput, error) {
+	return &m.DeleteFirewallPolicyOutput, nil
+}
+
+func (m mockedNetworkFirewallPolicy) ListFirewallPolicies(*networkfirewall.ListFirewallPoliciesInput) (*networkfirewall.ListFirewallPoliciesOutput, error) {
+	return &m.ListFirewallPoliciesOutput, nil
+}
+
+func (m mockedNetworkFirewallPolicy) DescribeFirewallPolicy(req *networkfirewall.DescribeFirewallPolicyInput) (*networkfirewall.DescribeFirewallPolicyOutput, error) {
+	raw := awsgo.StringValue(req.FirewallPolicyArn)
+	v, ok := m.DescribeFirewallPolicyOutput[raw]
+	if !ok {
+		return nil, fmt.Errorf("unable to describe the %s", raw)
+	}
+	return &v, nil
+}
+
+func TestNetworkFirewallPolicy_GetAll(t *testing.T) {
+
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "test-network-firewall-id1"
+		testId2   = "test-network-firewall-id2"
+		testName1 = "test-network-firewall-1"
+		testName2 = "test-network-firewall-2"
+	)
+
+	nfw := NetworkFirewallPolicy{
+		Client: mockedNetworkFirewallPolicy{
+			ListFirewallPoliciesOutput: networkfirewall.ListFirewallPoliciesOutput{
+				FirewallPolicies: []*networkfirewall.FirewallPolicyMetadata{
+					{
+						Arn:  awsgo.String(testId1),
+						Name: awsgo.String(testName1),
+					},
+					{
+						Arn:  awsgo.String(testId2),
+						Name: awsgo.String(testName2),
+					},
+				},
+			},
+			DescribeFirewallPolicyOutput: map[string]networkfirewall.DescribeFirewallPolicyOutput{
+				testId1: {
+					FirewallPolicyResponse: &networkfirewall.FirewallPolicyResponse{
+						FirewallPolicyName: awsgo.String(testName1),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+				testId2: {
+					FirewallPolicyResponse: &networkfirewall.FirewallPolicyResponse{
+						FirewallPolicyName: awsgo.String(testName2),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	nfw.BaseAwsResource.Init(nil)
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testName1, testName2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testName2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: awsgo.Time(now),
+				}},
+			expected: []string{testName1},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			names, err := nfw.getAll(context.Background(), config.Config{
+				NetworkFirewallPolicy: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+		})
+	}
+}
+
+func TestNetworkFirewallPolicy_NukeAll(t *testing.T) {
+
+	t.Parallel()
+
+	ngw := NetworkFirewallPolicy{
+		Client: mockedNetworkFirewallPolicy{
+			DeleteFirewallPolicyOutput: networkfirewall.DeleteFirewallPolicyOutput{},
+		},
+	}
+
+	err := ngw.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/network_firewall_resource_policy_test.go b/aws/resources/network_firewall_resource_policy_test.go
new file mode 100644
index 0000000..6d20658
--- /dev/null
+++ b/aws/resources/network_firewall_resource_policy_test.go
@@ -0,0 +1,122 @@
+package resources
+
+import (
+	"context"
+	"regexp"
+	"testing"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedNetworkFirewallResourcePolicy struct {
+	networkfirewalliface.NetworkFirewallAPI
+	ListFirewallPoliciesOutput   networkfirewall.ListFirewallPoliciesOutput
+	ListRuleGroupsOutput         networkfirewall.ListRuleGroupsOutput
+	DeleteResourcePolicyOutput   networkfirewall.DeleteResourcePolicyOutput
+	DescribeResourcePolicyOutput networkfirewall.DescribeResourcePolicyOutput
+}
+
+func (m mockedNetworkFirewallResourcePolicy) ListFirewallPolicies(*networkfirewall.ListFirewallPoliciesInput) (*networkfirewall.ListFirewallPoliciesOutput, error) {
+	return &m.ListFirewallPoliciesOutput, nil
+}
+
+func (m mockedNetworkFirewallResourcePolicy) ListRuleGroups(*networkfirewall.ListRuleGroupsInput) (*networkfirewall.ListRuleGroupsOutput, error) {
+	return &m.ListRuleGroupsOutput, nil
+}
+func (m mockedNetworkFirewallResourcePolicy) DeleteResourcePolicy(*networkfirewall.DeleteResourcePolicyInput) (*networkfirewall.DeleteResourcePolicyOutput, error) {
+	return &m.DeleteResourcePolicyOutput, nil
+}
+
+func (m mockedNetworkFirewallResourcePolicy) DescribeResourcePolicy(*networkfirewall.DescribeResourcePolicyInput) (*networkfirewall.DescribeResourcePolicyOutput, error) {
+	return &m.DescribeResourcePolicyOutput, nil
+}
+
+func TestNetworkFirewallResourcePolicy_GetAll(t *testing.T) {
+
+	t.Parallel()
+
+	var (
+		policy1 = "test-network-firewall-policy-1"
+		policy2 = "test-network-firewall-policy-2"
+		group1  = "test-network-firewall-group-1"
+		group2  = "test-network-firewall-group-2"
+	)
+
+	nfw := NetworkFirewallResourcePolicy{
+		Client: mockedNetworkFirewallResourcePolicy{
+			ListFirewallPoliciesOutput: networkfirewall.ListFirewallPoliciesOutput{
+				FirewallPolicies: []*networkfirewall.FirewallPolicyMetadata{
+					{
+						Arn: awsgo.String(policy1),
+					},
+					{
+						Arn: awsgo.String(policy2),
+					},
+				},
+			},
+			ListRuleGroupsOutput: networkfirewall.ListRuleGroupsOutput{
+				RuleGroups: []*networkfirewall.RuleGroupMetadata{
+					{
+						Arn: awsgo.String(group1),
+					},
+					{
+						Arn: awsgo.String(group2),
+					},
+				},
+			},
+			DescribeResourcePolicyOutput: networkfirewall.DescribeResourcePolicyOutput{
+				Policy: aws.String("policy-statements"),
+			},
+		},
+	}
+
+	nfw.BaseAwsResource.Init(nil)
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{policy1, policy2, group1, group2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(policy1),
+					}}},
+			},
+			expected: []string{policy2, group1, group2},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			names, err := nfw.getAll(context.Background(), config.Config{
+				NetworkFirewallResourcePolicy: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+		})
+	}
+}
+
+func TestNetworkFirewallResourcePolicy_NukeAll(t *testing.T) {
+
+	t.Parallel()
+
+	ngw := NetworkFirewallResourcePolicy{
+		Client: mockedNetworkFirewallResourcePolicy{
+			DeleteResourcePolicyOutput: networkfirewall.DeleteResourcePolicyOutput{},
+		},
+	}
+
+	err := ngw.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/network_firewall_rule_group_test.go b/aws/resources/network_firewall_rule_group_test.go
new file mode 100644
index 0000000..3c46ac2
--- /dev/null
+++ b/aws/resources/network_firewall_rule_group_test.go
@@ -0,0 +1,172 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedNetworkFirewallRuleGroup struct {
+	networkfirewalliface.NetworkFirewallAPI
+	DescribeRuleGroupOutput map[string]networkfirewall.DescribeRuleGroupOutput
+	TagResourceOutput       networkfirewall.TagResourceOutput
+	DeleteRuleGroupOutput   networkfirewall.DeleteRuleGroupOutput
+	ListRuleGroupsOutput    networkfirewall.ListRuleGroupsOutput
+}
+
+func (m mockedNetworkFirewallRuleGroup) TagResource(*networkfirewall.TagResourceInput) (*networkfirewall.TagResourceOutput, error) {
+	return &m.TagResourceOutput, nil
+}
+
+func (m mockedNetworkFirewallRuleGroup) DescribeRuleGroup(req *networkfirewall.DescribeRuleGroupInput) (*networkfirewall.DescribeRuleGroupOutput, error) {
+	raw := awsgo.StringValue(req.RuleGroupArn)
+	v, ok := m.DescribeRuleGroupOutput[raw]
+	if !ok {
+		return nil, fmt.Errorf("unable to describe the %s", raw)
+	}
+	return &v, nil
+}
+
+func (m mockedNetworkFirewallRuleGroup) DeleteRuleGroup(*networkfirewall.DeleteRuleGroupInput) (*networkfirewall.DeleteRuleGroupOutput, error) {
+	return &m.DeleteRuleGroupOutput, nil
+}
+
+func (m mockedNetworkFirewallRuleGroup) ListRuleGroups(*networkfirewall.ListRuleGroupsInput) (*networkfirewall.ListRuleGroupsOutput, error) {
+	return &m.ListRuleGroupsOutput, nil
+}
+
+func TestNetworkFirewallRuleGroup_GetAll(t *testing.T) {
+
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "test-network-rule-group-id1"
+		testId2   = "test-network-firewall-id2"
+		testName1 = "test-network-firewall-1"
+		testName2 = "test-network-firewall-2"
+	)
+
+	nfw := NetworkFirewallRuleGroup{
+		RuleGroups: make(map[string]RuleGroup),
+		Client: mockedNetworkFirewallRuleGroup{
+			ListRuleGroupsOutput: networkfirewall.ListRuleGroupsOutput{
+				RuleGroups: []*networkfirewall.RuleGroupMetadata{
+					{
+						Arn:  awsgo.String(testId1),
+						Name: awsgo.String(testName1),
+					},
+					{
+						Arn:  awsgo.String(testId2),
+						Name: awsgo.String(testName2),
+					},
+				},
+			},
+			DescribeRuleGroupOutput: map[string]networkfirewall.DescribeRuleGroupOutput{
+				testId1: {
+					RuleGroupResponse: &networkfirewall.RuleGroupResponse{
+						RuleGroupName: awsgo.String(testName1),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+				testId2: {
+					RuleGroupResponse: &networkfirewall.RuleGroupResponse{
+						RuleGroupName: awsgo.String(testName2),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	nfw.BaseAwsResource.Init(nil)
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testName1, testName2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testName2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: awsgo.Time(now),
+				}},
+			expected: []string{testName1},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			names, err := nfw.getAll(context.Background(), config.Config{
+				NetworkFirewallRuleGroup: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+		})
+	}
+}
+
+func TestNetworkFirewallRuleGroup_NukeAll(t *testing.T) {
+
+	t.Parallel()
+
+	ngw := NetworkFirewallRuleGroup{
+		Client: mockedNetworkFirewallRuleGroup{
+			DeleteRuleGroupOutput: networkfirewall.DeleteRuleGroupOutput{},
+		},
+		RuleGroups: map[string]RuleGroup{
+			"test-001": {
+				Name: awsgo.String("test-001"),
+				Type: awsgo.String("stateless"),
+			},
+			"test-002": {
+				Name: awsgo.String("test-002"),
+				Type: awsgo.String("stateless"),
+			},
+		},
+	}
+
+	err := ngw.nukeAll([]*string{
+		aws.String("test-001"),
+		aws.String("test-002"),
+	})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/network_firewall_test.go b/aws/resources/network_firewall_test.go
new file mode 100644
index 0000000..015c9b8
--- /dev/null
+++ b/aws/resources/network_firewall_test.go
@@ -0,0 +1,158 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedNetworkFirewall struct {
+	networkfirewalliface.NetworkFirewallAPI
+	DeleteFirewallOutput   networkfirewall.DeleteFirewallOutput
+	ListFirewallsOutput    networkfirewall.ListFirewallsOutput
+	TagResourceOutput      networkfirewall.TagResourceOutput
+	DescribeFirewallOutput map[string]networkfirewall.DescribeFirewallOutput
+}
+
+func (m mockedNetworkFirewall) TagResource(*networkfirewall.TagResourceInput) (*networkfirewall.TagResourceOutput, error) {
+	return &m.TagResourceOutput, nil
+}
+
+func (m mockedNetworkFirewall) DeleteFirewall(*networkfirewall.DeleteFirewallInput) (*networkfirewall.DeleteFirewallOutput, error) {
+	return &m.DeleteFirewallOutput, nil
+}
+
+func (m mockedNetworkFirewall) ListFirewalls(*networkfirewall.ListFirewallsInput) (*networkfirewall.ListFirewallsOutput, error) {
+	return &m.ListFirewallsOutput, nil
+}
+
+func (m mockedNetworkFirewall) DescribeFirewall(req *networkfirewall.DescribeFirewallInput) (*networkfirewall.DescribeFirewallOutput, error) {
+	raw := awsgo.StringValue(req.FirewallArn)
+	v, ok := m.DescribeFirewallOutput[raw]
+	if !ok {
+		return nil, fmt.Errorf("unable to describe the %s", raw)
+	}
+	return &v, nil
+}
+
+func TestNetworkFirewall_GetAll(t *testing.T) {
+
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "test-network-firewall-id1"
+		testId2   = "test-network-firewall-id2"
+		testName1 = "test-network-firewall-1"
+		testName2 = "test-network-firewall-2"
+	)
+
+	nfw := NetworkFirewall{
+		Client: mockedNetworkFirewall{
+			ListFirewallsOutput: networkfirewall.ListFirewallsOutput{
+				Firewalls: []*networkfirewall.FirewallMetadata{
+					{
+						FirewallArn:  awsgo.String(testId1),
+						FirewallName: awsgo.String(testName1),
+					},
+					{
+						FirewallArn:  awsgo.String(testId2),
+						FirewallName: awsgo.String(testName2),
+					},
+				},
+			},
+			DescribeFirewallOutput: map[string]networkfirewall.DescribeFirewallOutput{
+				testId1: {
+					Firewall: &networkfirewall.Firewall{
+						FirewallName: awsgo.String(testName1),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+				testId2: {
+					Firewall: &networkfirewall.Firewall{
+						FirewallName: awsgo.String(testName2),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	nfw.BaseAwsResource.Init(nil)
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testName1, testName2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testName2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: awsgo.Time(now),
+				}},
+			expected: []string{testName1},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			names, err := nfw.getAll(context.Background(), config.Config{
+				NetworkFirewall: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+		})
+	}
+}
+
+func TestNetworkFirewall_NukeAll(t *testing.T) {
+
+	t.Parallel()
+
+	ngw := NetworkFirewall{
+		Client: mockedNetworkFirewall{
+			DeleteFirewallOutput: networkfirewall.DeleteFirewallOutput{},
+		},
+	}
+
+	err := ngw.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/network_firewall_tls_config_test.go b/aws/resources/network_firewall_tls_config_test.go
new file mode 100644
index 0000000..c961998
--- /dev/null
+++ b/aws/resources/network_firewall_tls_config_test.go
@@ -0,0 +1,161 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedNetworkFirewallTLSConfig struct {
+	networkfirewalliface.NetworkFirewallAPI
+	DescribeTLSInspectionConfigurationOutput map[string]networkfirewall.DescribeTLSInspectionConfigurationOutput
+	TagResourceOutput                        networkfirewall.TagResourceOutput
+	DeleteTLSInspectionConfigurationOutput   networkfirewall.DeleteTLSInspectionConfigurationOutput
+	ListTLSInspectionConfigurationsOutput    networkfirewall.ListTLSInspectionConfigurationsOutput
+}
+
+func (m mockedNetworkFirewallTLSConfig) TagResource(*networkfirewall.TagResourceInput) (*networkfirewall.TagResourceOutput, error) {
+	return &m.TagResourceOutput, nil
+}
+
+func (m mockedNetworkFirewallTLSConfig) DescribeTLSInspectionConfiguration(req *networkfirewall.DescribeTLSInspectionConfigurationInput) (*networkfirewall.DescribeTLSInspectionConfigurationOutput, error) {
+	raw := awsgo.StringValue(req.TLSInspectionConfigurationArn)
+	v, ok := m.DescribeTLSInspectionConfigurationOutput[raw]
+	if !ok {
+		return nil, fmt.Errorf("unable to describe the %s", raw)
+	}
+	return &v, nil
+}
+
+func (m mockedNetworkFirewallTLSConfig) DeleteTLSInspectionConfiguration(*networkfirewall.DeleteTLSInspectionConfigurationInput) (*networkfirewall.DeleteTLSInspectionConfigurationOutput, error) {
+	return &m.DeleteTLSInspectionConfigurationOutput, nil
+}
+
+func (m mockedNetworkFirewallTLSConfig) ListTLSInspectionConfigurations(*networkfirewall.ListTLSInspectionConfigurationsInput) (*networkfirewall.ListTLSInspectionConfigurationsOutput, error) {
+	return &m.ListTLSInspectionConfigurationsOutput, nil
+}
+
+func TestNetworkFirewallTLSConfig_GetAll(t *testing.T) {
+
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "test-network-firewall-id1"
+		testId2   = "test-network-firewall-id2"
+		testName1 = "test-network-firewall-1"
+		testName2 = "test-network-firewall-2"
+	)
+
+	nfw := NetworkFirewallTLSConfig{
+		Client: mockedNetworkFirewallTLSConfig{
+			ListTLSInspectionConfigurationsOutput: networkfirewall.ListTLSInspectionConfigurationsOutput{
+				TLSInspectionConfigurations: []*networkfirewall.TLSInspectionConfigurationMetadata{
+					{
+						Arn:  awsgo.String(testId1),
+						Name: awsgo.String(testName1),
+					},
+					{
+						Arn:  awsgo.String(testId2),
+						Name: awsgo.String(testName2),
+					},
+				},
+			},
+			DescribeTLSInspectionConfigurationOutput: map[string]networkfirewall.DescribeTLSInspectionConfigurationOutput{
+				testId1: {
+					TLSInspectionConfigurationResponse: &networkfirewall.TLSInspectionConfigurationResponse{
+						TLSInspectionConfigurationName: awsgo.String(testName1),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+				testId2: {
+					TLSInspectionConfigurationResponse: &networkfirewall.TLSInspectionConfigurationResponse{
+						TLSInspectionConfigurationName: awsgo.String(testName2),
+						Tags: []*networkfirewall.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							}, {
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	nfw.BaseAwsResource.Init(nil)
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testName1, testName2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testName2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: awsgo.Time(now),
+				}},
+			expected: []string{testName1},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			names, err := nfw.getAll(context.Background(), config.Config{
+				NetworkFirewallTLSConfig: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+		})
+	}
+}
+
+func TestNetworkFirewallTLSConfig_NukeAll(t *testing.T) {
+
+	t.Parallel()
+
+	ngw := NetworkFirewallTLSConfig{
+		Client: mockedNetworkFirewallTLSConfig{
+			DeleteTLSInspectionConfigurationOutput: networkfirewall.DeleteTLSInspectionConfigurationOutput{},
+		},
+	}
+
+	err := ngw.nukeAll([]*string{
+		aws.String("test-001"),
+		aws.String("test-002"),
+	})
+	require.NoError(t, err)
+}
diff --git a/config/config_test.go b/config/config_test.go
index 2b57bdb..75c7bc5 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -103,6 +103,11 @@ func emptyConfig() *Config {
 		ResourceType{FilterRule{}, FilterRule{}, ""},
 		ResourceType{FilterRule{}, FilterRule{}, ""},
 		EC2ResourceType{false, ResourceType{FilterRule{}, FilterRule{}, ""}},
+		ResourceType{FilterRule{}, FilterRule{}, ""},
+		ResourceType{FilterRule{}, FilterRule{}, ""},
+		ResourceType{FilterRule{}, FilterRule{}, ""},
+		ResourceType{FilterRule{}, FilterRule{}, ""},
+		ResourceType{FilterRule{}, FilterRule{}, ""},
 	}
 }