Repository: opensearch-project/opensearch-go
Pull request #519 (2024-04-08 23:09:22)

Bump github.com/aws/aws-sdk-go from 1.51.16 to 1.51.17
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.51.16 to 1.51.17.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/aws/aws-sdk-go/releases">github.com/aws/aws-sdk-go's releases</a>.</em></p>
<blockquote>
<h1>Release v1.51.17 (2024-04-08)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/controlcatalog</code>: Adds new service</li>
<li><code>service/mgn</code>: Updates service API and documentation</li>
<li><code>service/networkmonitor</code>: Updates service API and documentation</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/aws/aws-sdk-go/commit/76ef9d5877d5544edc061b4a5cc22fe53e81f1f7"><code>76ef9d5</code></a> Release v1.51.17 (2024-04-08) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/5220">#5220</a>)</li>
<li>See full diff in <a href="https://github.com/aws/aws-sdk-go/compare/v1.51.16...v1.51.17">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go&package-manager=go_modules&previous-version=1.51.16&new-version=1.51.17)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.ci/opensearch/Dockerfile.opensearch b/.ci/opensearch/Dockerfile.opensearch
index 863a76d..2ab4a2c 100755
--- a/.ci/opensearch/Dockerfile.opensearch
+++ b/.ci/opensearch/Dockerfile.opensearch
@@ -1,25 +1,27 @@
 ARG OPENSEARCH_VERSION
 FROM opensearchproject/opensearch:${OPENSEARCH_VERSION}
 
-ARG OPENSEARCH_VERSION
 ARG opensearch_path=/usr/share/opensearch
 ARG SECURE_INTEGRATION
 ENV SECURE_INTEGRATION=$SECURE_INTEGRATION
+ARG OPENSEARCH_INITIAL_ADMIN_PASSWORD
 
-# Starting in 2.12.0 security demo requires an initial admin password, which is set as myStrongPassword123!
+# Some opensearch secuirty settings are only present since 2.8.0 and causes older versions to brake if the setting is present
 # https://apple.stackexchange.com/a/123408/11374
 RUN if [ "$SECURE_INTEGRATION" != "true" ] ; then \
       $opensearch_path/bin/opensearch-plugin remove opensearch-security; \
       else \
+        $opensearch_path/opensearch-onetime-setup.sh; \
+        echo "plugins.security.nodes_dn_dynamic_config_enabled: true"  | tee -a $opensearch_path/config/opensearch.yml > /dev/null; \
+        echo "plugins.security.unsupported.restapi.allow_securityconfig_modification: true"  | tee -a $opensearch_path/config/opensearch.yml > /dev/null; \
+        echo "plugins.security.ssl_cert_reload_enabled: true" | tee -a $opensearch_path/config/opensearch.yml > /dev/null; \
         function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }; \
-        if [ $(version $OPENSEARCH_VERSION) -ge $(version "2.12.0") ] || [ $OPENSEARCH_VERSION == "latest" ]; then \
-          echo user admin:myStrongPassword123! > curl.conf ; \
-        else \
-          echo user admin:admin > curl.conf ; \
-        fi\
+        if [ $(version $OPENSEARCH_VERSION) -ge $(version "2.8.0") ] || [ $OPENSEARCH_VERSION == "latest" ]; then \
+          echo "plugins.security.restapi.admin.enabled: true" | tee -a $opensearch_path/config/opensearch.yml > /dev/null; \
+        fi \
       fi
 
 HEALTHCHECK --start-period=20s --interval=30s \
   CMD curl -sf -retry 5 --max-time 5 --retry-delay 5 --retry-max-time 30 \
-  $(if $SECURE_INTEGRATION; then echo "-K curl.conf -k https://"; fi)"localhost:9200" \
+  $(if $SECURE_INTEGRATION; then echo "--cert config/kirk.pem --key config/kirk-key.pem -k https://"; fi)"localhost:9200" \
   || bash -c 'kill -s 15 -1 && (sleep 10; kill -s 9 -1)'
diff --git a/.ci/opensearch/docker-compose.yml b/.ci/opensearch/docker-compose.yml
index d7421f9..1754359 100755
--- a/.ci/opensearch/docker-compose.yml
+++ b/.ci/opensearch/docker-compose.yml
@@ -11,6 +11,7 @@ services:
       args:
         - SECURE_INTEGRATION=${SECURE_INTEGRATION:-false}
         - OPENSEARCH_VERSION=${OPENSEARCH_VERSION:-latest}
+        - OPENSEARCH_INITIAL_ADMIN_PASSWORD=myStrongPassword123!
     environment:
       - discovery.type=single-node
       - bootstrap.memory_lock=true
diff --git a/.golangci.yml b/.golangci.yml
index 9d30322..7c017af 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -172,4 +172,4 @@ issues:
       path: opensearchtransport/opensearchtransport.go
     - linters:
         - dupl
-      path: (-params\.go|api_indices|api_dangling\.go|api_point_in_time\.go|rethrottle\.go|api_cat-.*\.go)
+      path: (-params\.go|api_indices|api_dangling\.go|api_point_in_time\.go|rethrottle\.go|api_cat-.*\.go|plugins/security/api_\w+.go|plugins/security/api_.*-patch.go)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 765f8e7..6a459ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,30 +7,41 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 ### Added
 - Adds GlobalIOUsage struct for nodes stats ([#506](https://github.com/opensearch-project/opensearch-go/pull/506))
 - Adds the `Explanation` field containing the document explain details to the `SearchHit` struct. ([#504](https://github.com/opensearch-project/opensearch-go/pull/504))
-- Adds the `Fields` field containing the document fields to the `SearchHit` struct. ([#508](https://github.com/opensearch-project/opensearch-go/pull/508))
 - Adds new error types ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
 - Adds handling of non json errors to ParseError ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Adds the `Failures` field to opensearchapi structs ([#510](https://github.com/opensearch-project/opensearch-go/pull/510))
+- Adds the `Fields` field containing the document fields to the `SearchHit` struct. ([#508](https://github.com/opensearch-project/opensearch-go/pull/508))
+- Adds security plugin ([#507](https://github.com/opensearch-project/opensearch-go/pull/507))
+- Adds security settings to container for security testing ([#507](https://github.com/opensearch-project/opensearch-go/pull/507))
+- Adds cluster.get-certs to copy admin certs out of the container ([#507](https://github.com/opensearch-project/opensearch-go/pull/507))
 
 ### Changed
-- Use docker compose v2 instead of v1 ([#506](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Uses docker compose v2 instead of v1 ([#506](https://github.com/opensearch-project/opensearch-go/pull/506))
 - Updates go version to 1.21 ([#509](https://github.com/opensearch-project/opensearch-go/pull/509))
-- Moved Error structs from opensearchapi to opensearch package ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
-- Move parseError function from opensearchapi to opensearch package as ParseError ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
-- Change ParseError function to do type assertion to determine error type ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Moves Error structs from opensearchapi to opensearch package ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Moves parseError function from opensearchapi to opensearch package as ParseError ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Changes ParseError function to do type assertion to determine error type ([#512](https://github.com/opensearch-project/opensearch-go/pull/506))
+- Removes unused structs and functions from opensearch ([#517](https://github.com/opensearch-project/opensearch-go/pull/517))
+- Adjusts and extent opensearch tests for better coverage ([#517](https://github.com/opensearch-project/opensearch-go/pull/517))
+- Bumps codecov action version to v4 ([#517](https://github.com/opensearch-project/opensearch-go/pull/517))
+- Changes bulk error/reason field and some cat response fields to pointer as they can be nil ([#510](https://github.com/opensearch-project/opensearch-go/pull/510))
+- Adjust workflows to work with security plugin ([#507](https://github.com/opensearch-project/opensearch-go/pull/507))
+- Updates USER_GUIDE.md and /_samples/ ([#518](https://github.com/opensearch-project/opensearch-go/pull/518))
 
 ### Deprecated
 
 ### Removed
 
 ### Fixed
-- Fix search request missing a slash when no indices are given ([#470](https://github.com/opensearch-project/opensearch-go/pull/469))
+- Fixes search request missing a slash when no indices are given ([#470](https://github.com/opensearch-project/opensearch-go/pull/469))
+- Fixes opensearchtransport check for nil response body ([#517](https://github.com/opensearch-project/opensearch-go/pull/517))
 
 ### Security
 
 ### Dependencies
 - Bumps `github.com/aws/aws-sdk-go-v2` from 1.25.3 to 1.26.1
 - Bumps `github.com/wI2L/jsondiff` from 0.4.0 to 0.5.1
-- Bumps `github.com/aws/aws-sdk-go` from 1.50.36 to 1.51.16
+- Bumps `github.com/aws/aws-sdk-go` from 1.50.36 to 1.51.17
 - Bumps `github.com/aws/aws-sdk-go-v2/config` from 1.27.7 to 1.27.11
 
 ## [3.1.0]
@@ -185,4 +196,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 [2.1.0]: https://github.com/opensearch-project/opensearch-go/compare/v2.0.1...v2.1.0
 [2.0.1]: https://github.com/opensearch-project/opensearch-go/compare/v2.0.0...v2.0.1
 [2.0.0]: https://github.com/opensearch-project/opensearch-go/compare/v1.1.0...v2.0.0
-[1.0.0]: https://github.com/opensearch-project/opensearch-go/compare/v1.0.0...v1.1.0
\ No newline at end of file
+[1.0.0]: https://github.com/opensearch-project/opensearch-go/compare/v1.0.0...v1.1.0
diff --git a/Makefile b/Makefile
index 8e9fb38..c804f4e 100644
--- a/Makefile
+++ b/Makefile
@@ -197,10 +197,16 @@ cluster.build:
 	docker compose --project-directory .ci/opensearch build;
 
 cluster.start:
-	docker compose --project-directory .ci/opensearch up -d ;
+	docker compose --project-directory .ci/opensearch up -d;
 
 cluster.stop:
-	docker compose --project-directory .ci/opensearch down ;
+	docker compose --project-directory .ci/opensearch down;
+
+cluster.get-cert:
+	@if [[ -v SECURE_INTEGRATION ]] && [[ $$SECURE_INTEGRATION == "true" ]]; then \
+		docker cp $$(docker compose --project-directory .ci/opensearch ps --format '{{.Name}}'):/usr/share/opensearch/config/kirk.pem admin.pem && \
+		docker cp $$(docker compose --project-directory .ci/opensearch ps --format '{{.Name}}'):/usr/share/opensearch/config/kirk-key.pem admin.key; \
+	fi
 
 
 cluster.clean: ## Remove unused Docker volumes and networks
diff --git a/USER_GUIDE.md b/USER_GUIDE.md
index 59d033d..f0cbb4f 100644
--- a/USER_GUIDE.md
+++ b/USER_GUIDE.md
@@ -32,7 +32,7 @@ const IndexName = "go-test-index1"
 
 func main() {
 	if err := example(); err != nil {
-		fmt.Println(fmt.Sprintf("Error: %s", err))
+		fmt.Printf("Error: %s\n", err)
 		os.Exit(1)
 	}
 }
@@ -47,7 +47,7 @@ func example() error {
 				},
 				Addresses: []string{"https://localhost:9200"},
 				Username:  "admin", // For testing only. Don't store credentials in code.
-				Password:  "admin",
+				Password:  "myStrongPassword123!",
 			},
 		},
 	)
@@ -64,8 +64,8 @@ func example() error {
 	fmt.Printf("Cluster INFO:\n  Cluster Name: %s\n  Cluster UUID: %s\n  Version Number: %s\n", infoResp.ClusterName, infoResp.ClusterUUID, infoResp.Version.Number)
 
 	// Define index mapping.
-        // Note: these particular settings (eg, shards/replicas)
-        // will have no effect in AWS OpenSearch Serverless
+	// Note: these particular settings (eg, shards/replicas)
+	// will have no effect in AWS OpenSearch Serverless
 	mapping := strings.NewReader(`{
 	    "settings": {
 	        "index": {
@@ -75,15 +75,17 @@ func example() error {
 	}`)
 
 	// Create an index with non-default settings.
-	var opensearchError opensearchapi.Error
 	createIndexResponse, err := client.Indices.Create(
-        ctx,
-	    opensearchapi.IndicesCreateReq{
-	        Index: IndexName,
-            Body:  mapping,
-	    },
-    )
-	// Load err into opensearchapi.Error to access the fields and tolerate if the index already exists
+		ctx,
+		opensearchapi.IndicesCreateReq{
+			Index: IndexName,
+			Body:  mapping,
+		},
+	)
+
+	var opensearchError *opensearch.StructError
+
+	// Load err into opensearch.Error to access the fields and tolerate if the index already exists
 	if err != nil {
 		if errors.As(err, &opensearchError) {
 			if opensearchError.Err.Type != "resource_already_exists_exception" {
@@ -108,13 +110,16 @@ func example() error {
 
 	docId := "1"
 	insertResp, err := client.Index(
-        ctx,
-	    opensearchapi.IndexReq{
-	        Index:      IndexName,
-	        DocumentID: docId,
-            Body:       opensearchutil.NewJSONReader(&document),
-	    }
-    )
+		ctx,
+		opensearchapi.IndexReq{
+			Index:      IndexName,
+			DocumentID: docId,
+			Body:       opensearchutil.NewJSONReader(&document),
+			Params: opensearchapi.IndexParams{
+				Refresh: "true",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
@@ -122,7 +127,7 @@ func example() error {
 
 	// Search for the document.
 	content := strings.NewReader(`{
-	    "size": 5,
+		"size": 5,
 	    "query": {
 	        "multi_match": {
 	            "query": "miller",
@@ -132,14 +137,16 @@ func example() error {
 	}`)
 
 	searchResp, err := client.Search(
-        ctx,
-	    &opensearchapi.SearchReq{
-	        Body: content,
-	    }
-    )
+		ctx,
+		&opensearchapi.SearchReq{
+			Body: content,
+		},
+	)
 	if err != nil {
 		return err
 	}
+	fmt.Printf("Search hits: %v\n", searchResp.Hits.Total.Value)
+
 	if searchResp.Hits.Total.Value > 0 {
 		indices := make([]string, 0)
 		for _, hit := range searchResp.Hits.Hits {
@@ -156,35 +163,35 @@ func example() error {
 		fmt.Printf("Search indices: %s\n", strings.Join(indices, ","))
 	}
 
-	/*
-		// Delete the document.
-		deleteReq := opensearchapi.DeleteReq{
-			Index:      IndexName,
-			DocumentID: docId,
-		}
+	// Delete the document.
+	deleteReq := opensearchapi.DocumentDeleteReq{
+		Index:      IndexName,
+		DocumentID: docId,
+	}
+
+	deleteResponse, err := client.Document.Delete(ctx, deleteReq)
+	if err != nil {
+		return err
+	}
+	fmt.Printf("Deleted document: %t\n", deleteResponse.Result == "deleted")
 
-		deleteResponse, err := client.Indices.Delete(ctx, deleteReq)
-		if err != nil {
-			return err
-		}
-		fmt.Println("deleting document")
-		fmt.Println(deleteResponse)
-	*/
 	// Delete previously created index.
+	deleteIndex := opensearchapi.IndicesDeleteReq{Indices: []string{IndexName}}
 
-	deleteIndexResp, err := client.Indices.Delete(ctx, opensearchapi.IndicesDeleteReq{Index: []string{IndexName}})
+	deleteIndexResp, err := client.Indices.Delete(ctx, deleteIndex)
 	if err != nil {
 		return err
 	}
-	fmt.Println(fmt.Sprintf("Deleted index: %t", deleteIndexResp.Acknowledged))
+	fmt.Printf("Deleted index: %t\n", deleteIndexResp.Acknowledged)
 
-	// Try to delete the index again which failes as it does not exist
-	// Load err into opensearchapi.Error to access the fields and tolerate if the index is missing
+	// Try to delete the index again which fails as it does not exist
 	_, err = client.Indices.Delete(ctx, deleteIndex)
+
+	// Load err into opensearchapi.Error to access the fields and tolerate if the index is missing
 	if err != nil {
 		if errors.As(err, &opensearchError) {
 			if opensearchError.Err.Type != "index_not_found_exception" {
-				return nil
+				return err
 			}
 		} else {
 			return err
@@ -192,6 +199,7 @@ func example() error {
 	}
 	return nil
 }
+
 ```
 
 ## Amazon OpenSearch Service
diff --git a/_samples/advanced_index_actions.go b/_samples/advanced_index_actions.go
index 04fda67..7a77730 100644
--- a/_samples/advanced_index_actions.go
+++ b/_samples/advanced_index_actions.go
@@ -7,10 +7,13 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -22,7 +25,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/bulk.go b/_samples/bulk.go
index 95d5ef2..517a854 100644
--- a/_samples/bulk.go
+++ b/_samples/bulk.go
@@ -7,11 +7,14 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -23,7 +26,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/data_stream.go b/_samples/data_stream.go
index 6ede51f..4802941 100644
--- a/_samples/data_stream.go
+++ b/_samples/data_stream.go
@@ -7,11 +7,14 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -23,7 +26,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/document_lifecycle.go b/_samples/document_lifecycle.go
index 233e213..9704334 100644
--- a/_samples/document_lifecycle.go
+++ b/_samples/document_lifecycle.go
@@ -7,11 +7,14 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -23,7 +26,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/index_lifecycle.go b/_samples/index_lifecycle.go
index a2c5329..fdc1406 100644
--- a/_samples/index_lifecycle.go
+++ b/_samples/index_lifecycle.go
@@ -7,11 +7,14 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -23,7 +26,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/json.go b/_samples/json.go
index 61215c1..1de9207 100644
--- a/_samples/json.go
+++ b/_samples/json.go
@@ -6,12 +6,14 @@
 package main
 
 import (
+	"crypto/tls"
 	"fmt"
 	"io"
 	"net/http"
 	"os"
 	"strings"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -25,7 +27,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/_samples/search.go b/_samples/search.go
index beef628..bfcd667 100644
--- a/_samples/search.go
+++ b/_samples/search.go
@@ -7,13 +7,16 @@ package main
 
 import (
 	"context"
+	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strconv"
 	"strings"
 	"time"
 
+	"github.com/opensearch-project/opensearch-go/v3"
 	"github.com/opensearch-project/opensearch-go/v3/opensearchapi"
 )
 
@@ -25,7 +28,19 @@ func main() {
 }
 
 func example() error {
-	client, err := opensearchapi.NewDefaultClient()
+	// Initialize the client with SSL/TLS enabled.
+	client, err := opensearchapi.NewClient(
+		opensearchapi.Config{
+			Client: opensearch.Config{
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // For testing only. Use certificate for validation.
+				},
+				Addresses: []string{"https://localhost:9200"},
+				Username:  "admin", // For testing only. Don't store credentials in code.
+				Password:  "myStrongPassword123!",
+			},
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/go.mod b/go.mod
index 0cc7a57..b67872b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/opensearch-project/opensearch-go/v3
 go 1.21
 
 require (
-	github.com/aws/aws-sdk-go v1.51.16
+	github.com/aws/aws-sdk-go v1.51.17
 	github.com/aws/aws-sdk-go-v2 v1.26.1
 	github.com/aws/aws-sdk-go-v2/config v1.27.11
 	github.com/stretchr/testify v1.9.0
diff --git a/go.sum b/go.sum
index cb2f835..524d68c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/aws/aws-sdk-go v1.51.16 h1:vnWKK8KjbftEkuPX8bRj3WHsLy1uhotn0eXptpvrxJI=
-github.com/aws/aws-sdk-go v1.51.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go v1.51.17 h1:Cfa40lCdjv9OxC3X1Ks3a6O1Tu3gOANSyKHOSw/zuWU=
+github.com/aws/aws-sdk-go v1.51.17/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
 github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
 github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
 github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
diff --git a/opensearch.go b/opensearch.go
index c990525..978941c 100644
--- a/opensearch.go
+++ b/opensearch.go
@@ -56,9 +56,6 @@ const (
 // Version returns the package version as a string.
 const Version = version.Client
 
-// SupportedElasticVersion defines the supported major elasticsearch version
-const SupportedElasticVersion = 7
-
 // Error vars
 var (
 	ErrCreateClient                        = errors.New("cannot create client")
@@ -112,17 +109,6 @@ type Client struct {
 	Transport opensearchtransport.Interface
 }
 
-type esVersion struct {
-	Number       string `json:"number"`
-	BuildFlavor  string `json:"build_flavor"`
-	Distribution string `json:"distribution"`
-}
-
-type info struct {
-	Version esVersion `json:"version"`
-	Tagline string    `json:"tagline"`
-}
-
 // NewDefaultClient creates a new client with default options.
 //
 // It will use http://localhost:9200 as the default address.
@@ -217,24 +203,6 @@ func getAddressFromEnvironment() []string {
 	return addrsFromEnvironment(envOpenSearchURL)
 }
 
-// checkCompatibleInfo validates the information given by OpenSearch
-func checkCompatibleInfo(info info) error {
-	major, _, _, err := ParseVersion(info.Version.Number)
-	if err != nil {
-		return err
-	}
-
-	if info.Version.Distribution == openSearch {
-		return nil
-	}
-
-	if major != SupportedElasticVersion {
-		return errors.New(unsupportedProduct)
-	}
-
-	return nil
-}
-
 // ParseVersion returns an int64 representation of version.
 func ParseVersion(version string) (int64, int64, int64, error) {
 	reVersion := regexp.MustCompile(`^([0-9]+)\.([0-9]+)\.([0-9]+)`)
@@ -294,13 +262,13 @@ func (c *Client) Do(ctx context.Context, req Request, dataPointer interface{}) (
 	if dataPointer != nil && resp.Body != nil && !response.IsError() {
 		data, err := io.ReadAll(resp.Body)
 		if err != nil {
-			return response, fmt.Errorf("failed to read the response body, status: %d, err: %w", resp.StatusCode, err)
+			return response, fmt.Errorf("%w, status: %d, err: %w", ErrReadBody, resp.StatusCode, err)
 		}
 
 		response.Body = io.NopCloser(bytes.NewReader(data))
 
 		if err := json.Unmarshal(data, dataPointer); err != nil {
-			return response, fmt.Errorf("failed to parse body into the pointer, status: %d, body: %s, err: %w", resp.StatusCode, data, err)
+			return response, fmt.Errorf("%w, status: %d, body: %s, err: %w", ErrJSONUnmarshalBody, resp.StatusCode, data, err)
 		}
 	}
 
@@ -357,3 +325,8 @@ func addrsToURLs(addrs []string) ([]*url.URL, error) {
 
 	return urls, nil
 }
+
+// ToPointer converts any value to a pointer, mainly used for request parameters
+func ToPointer[V any](value V) *V {
+	return &value
+}
diff --git a/opensearchapi/api_bulk.go b/opensearchapi/api_bulk.go
index d1683a5..4183ce1 100644
--- a/opensearchapi/api_bulk.go
+++ b/opensearchapi/api_bulk.go
@@ -96,8 +96,8 @@ type BulkRespItem struct {
 				End    int `json:"end"`
 			} `json:"position,omitempty"`
 			Cause *struct {
-				Type   string `json:"type"`
-				Reason string `json:"reason"`
+				Type   string  `json:"type"`
+				Reason *string `json:"reason"`
 			} `json:"caused_by"`
 		} `json:"caused_by,omitempty"`
 	} `json:"error,omitempty"`
diff --git a/opensearchapi/api_cat-nodeattrs.go b/opensearchapi/api_cat-nodeattrs.go
index 8f5cb6c..1f548be 100644
--- a/opensearchapi/api_cat-nodeattrs.go
+++ b/opensearchapi/api_cat-nodeattrs.go
@@ -39,7 +39,7 @@ type CatNodeAttrsResp struct {
 type CatNodeAttrsItemResp struct {
 	Node  string `json:"node"`
 	ID    string `json:"id"`
-	PID   int    `json:"pid,string"`
+	PID   *int   `json:"pid,string"`
 	Host  string `json:"host"`
 	IP    string `json:"ip"`
 	Port  int    `json:"port,string"`
diff --git a/opensearchapi/api_cat-nodes.go b/opensearchapi/api_cat-nodes.go
index f51da62..1f9275f 100644
--- a/opensearchapi/api_cat-nodes.go
+++ b/opensearchapi/api_cat-nodes.go
@@ -37,105 +37,105 @@ type CatNodesResp struct {
 
 // CatNodesItemResp represents one index of the CatNodesResp
 type CatNodesItemResp struct {
-	ID                              string `json:"id"`
-	PID                             string `json:"pid"`
-	IP                              string `json:"ip"`
-	Port                            int    `json:"port,string"`
-	HTTPAddress                     string `json:"http_address"`
-	Version                         string `json:"version"`
-	Type                            string `json:"type"`
-	Build                           string `json:"build"`
-	JDK                             string `json:"jdk"`
-	DiskTotal                       string `json:"disk.total"`
-	DiskUsed                        string `json:"disk.used"`
-	DiskAvail                       string `json:"disk.avail"`
-	DiskUsedPercent                 string `json:"disk.used_percent"`
-	HeapCurrent                     string `json:"heap.current"`
-	HeapPercent                     int    `json:"heap.percent,string"`
-	HeapMax                         string `json:"heap.max"`
-	RAMCurrent                      string `json:"ram.current"`
-	RAMPercent                      int    `json:"ram.percent,string"`
-	RAMMax                          string `json:"ram.max"`
-	FileDescCurrent                 int    `json:"file_desc.current,string"`
-	FileDescPercent                 int    `json:"file_desc.percent,string"`
-	FileDescMax                     int    `json:"file_desc.max,string"`
-	CPU                             int    `json:"cpu,string"`
-	Load1M                          string `json:"load_1m"`
-	Load5M                          string `json:"load_5m"`
-	Load15M                         string `json:"load_15m"`
-	Uptime                          string `json:"uptime"`
-	Role                            string `json:"node.role"`
-	Roles                           string `json:"node.roles"`
-	Master                          string `json:"master"`
-	ClusterManager                  string `json:"cluster_manager"`
-	Name                            string `json:"name"`
-	CompletionSize                  string `json:"completion.size"`
-	FieldDataMemorySize             string `json:"fielddata.memory_size"`
-	FileldDataEvictions             int    `json:"fielddata.evictions,string"`
-	QueryCacheMemorySize            string `json:"query_cache.memory_size"`
-	QueryCacheEvictions             int    `json:"query_cache.evictions,string"`
-	QueryCacheHitCount              int    `json:"query_cache.hit_count,string"`
-	QueryCacheMissCount             int    `json:"query_cache.miss_count,string"`
-	RequestCacheMemorySize          string `json:"request_cache.memory_size"`
-	RequestCacheEvictions           int    `json:"request_cache.evictions,string"`
-	RequestCacheHitCount            int    `json:"request_cache.hit_count,string"`
-	RequestCacheMissCount           int    `json:"request_cache.miss_count,string"`
-	FlushTotal                      int    `json:"flush.total,string"`
-	FlushTotalTime                  string `json:"flush.total_time"`
-	GetCurrent                      int    `json:"get.current,string"`
-	GetTime                         string `json:"get.time"`
-	GetTotal                        int    `json:"get.total,string"`
-	GetExistsTime                   string `json:"get.exists_time"`
-	GetExistsTotal                  int    `json:"get.exists_total,string"`
-	GetMissingTime                  string `json:"get.missing_time"`
-	GetMissingTotal                 int    `json:"get.missing_total,string"`
-	IndexingDeleteCurrent           int    `json:"indexing.delete_current,string"`
-	IndexingDeleteTime              string `json:"indexing.delete_time"`
-	IndexingDeleteTotal             int    `json:"indexing.delete_total,string"`
-	IndexingIndexCurrent            int    `json:"indexing.index_current,string"`
-	IndexingIndexTime               string `json:"indexing.index_time"`
-	IndexingIndexTotal              int    `json:"indexing.index_total,string"`
-	IndexingIndexFailed             int    `json:"indexing.index_failed,string"`
-	MergesCurrent                   int    `json:"merges.current,string"`
-	MergesCurrentDoc                int    `json:"merges.current_docs,string"`
-	MergesCurrentSize               string `json:"merges.current_size"`
-	MergesTotal                     int    `json:"merges.total,string"`
-	MergesTotalDocs                 int    `json:"merges.total_docs,string"`
-	MergesTotalSize                 string `json:"merges.total_size"`
-	MergesTotalTime                 string `json:"merges.total_time"`
-	RefreshTotal                    int    `json:"refresh.total,string"`
-	RefreshTime                     string `json:"refresh.time"`
-	RefreshExternalTotal            int    `json:"refresh.external_total,string"`
-	RefreshExternalTime             string `json:"refresh.external_time"`
-	RefreshListeners                int    `json:"refresh.listeners,string"`
-	ScriptCompilations              int    `json:"script.compilations,string"`
-	ScriptCacheEvictions            int    `json:"script.cache_evictions,string"`
-	ScriptCompilationLimitTriggered int    `json:"script.compilation_limit_triggered,string"`
-	SearchFetchCurrent              int    `json:"search.fetch_current,string"`
-	SearchFetchTime                 string `json:"search.fetch_time"`
-	SearchFetchTotal                int    `json:"search.fetch_total,string"`
-	SearchOpenContexts              int    `json:"search.open_contexts,string"`
-	SearchQueryCurrent              int    `json:"search.query_current,string"`
-	SearchQueryTime                 string `json:"search.query_time"`
-	SearchQueryTotal                int    `json:"search.query_total,string"`
-	SearchConcurrentQueryCurrent    int    `json:"search.concurrent_query_current,string"`
-	SearchConcurrentQueryTime       string `json:"search.concurrent_query_time"`
-	SearchConcurrentQueryTotal      int    `json:"search.concurrent_query_total,string"`
-	SearchConcurrentAvgSliceCount   string `json:"search.concurrent_avg_slice_count"`
-	SearchScrollCurrent             int    `json:"search.scroll_current,string"`
-	SearchScrollTime                string `json:"search.scroll_time"`
-	SearchScrollTotal               int    `json:"search.scroll_total,string"`
-	SearchPointInTimeCurrent        int    `json:"search.point_in_time_current,string"`
-	SearchPointInTimeTime           string `json:"search.point_in_time_time"`
-	SearchPointInTimeTotal          int    `json:"search.point_in_time_total,string"`
-	SegmentsCount                   int    `json:"segments.count,string"`
-	SegmentsMemory                  string `json:"segments.memory"`
-	SegmentsIndexWriteMemory        string `json:"segments.index_writer_memory"`
-	SegmentsVersionMapMemory        string `json:"segments.version_map_memory"`
-	SegmentsFixedBitsetMemory       string `json:"segments.fixed_bitset_memory"`
-	SuggestCurrent                  int    `json:"suggest.current,string"`
-	SuggestTime                     string `json:"suggest.time"`
-	SuggestTotal                    int    `json:"suggest.total,string"`
+	ID                              string  `json:"id"`
+	PID                             *string `json:"pid"`
+	IP                              string  `json:"ip"`
+	Port                            int     `json:"port,string"`
+	HTTPAddress                     string  `json:"http_address"`
+	Version                         string  `json:"version"`
+	Type                            *string `json:"type"`
+	Build                           *string `json:"build"`
+	JDK                             *string `json:"jdk"`
+	DiskTotal                       *string `json:"disk.total"`
+	DiskUsed                        *string `json:"disk.used"`
+	DiskAvail                       *string `json:"disk.avail"`
+	DiskUsedPercent                 *string `json:"disk.used_percent"`
+	HeapCurrent                     *string `json:"heap.current"`
+	HeapPercent                     *int    `json:"heap.percent,string"`
+	HeapMax                         *string `json:"heap.max"`
+	RAMCurrent                      *string `json:"ram.current"`
+	RAMPercent                      *int    `json:"ram.percent,string"`
+	RAMMax                          *string `json:"ram.max"`
+	FileDescCurrent                 *int    `json:"file_desc.current,string"`
+	FileDescPercent                 *int    `json:"file_desc.percent,string"`
+	FileDescMax                     *int    `json:"file_desc.max,string"`
+	CPU                             *int    `json:"cpu,string"`
+	Load1M                          *string `json:"load_1m"`
+	Load5M                          *string `json:"load_5m"`
+	Load15M                         *string `json:"load_15m"`
+	Uptime                          *string `json:"uptime"`
+	Role                            string  `json:"node.role"`
+	Roles                           string  `json:"node.roles"`
+	Master                          string  `json:"master"`
+	ClusterManager                  string  `json:"cluster_manager"`
+	Name                            string  `json:"name"`
+	CompletionSize                  *string `json:"completion.size"`
+	FieldDataMemorySize             *string `json:"fielddata.memory_size"`
+	FileldDataEvictions             *int    `json:"fielddata.evictions,string"`
+	QueryCacheMemorySize            *string `json:"query_cache.memory_size"`
+	QueryCacheEvictions             *int    `json:"query_cache.evictions,string"`
+	QueryCacheHitCount              *int    `json:"query_cache.hit_count,string"`
+	QueryCacheMissCount             *int    `json:"query_cache.miss_count,string"`
+	RequestCacheMemorySize          *string `json:"request_cache.memory_size"`
+	RequestCacheEvictions           *int    `json:"request_cache.evictions,string"`
+	RequestCacheHitCount            *int    `json:"request_cache.hit_count,string"`
+	RequestCacheMissCount           *int    `json:"request_cache.miss_count,string"`
+	FlushTotal                      *int    `json:"flush.total,string"`
+	FlushTotalTime                  *string `json:"flush.total_time"`
+	GetCurrent                      *int    `json:"get.current,string"`
+	GetTime                         *string `json:"get.time"`
+	GetTotal                        *int    `json:"get.total,string"`
+	GetExistsTime                   *string `json:"get.exists_time"`
+	GetExistsTotal                  *int    `json:"get.exists_total,string"`
+	GetMissingTime                  *string `json:"get.missing_time"`
+	GetMissingTotal                 *int    `json:"get.missing_total,string"`
+	IndexingDeleteCurrent           *int    `json:"indexing.delete_current,string"`
+	IndexingDeleteTime              *string `json:"indexing.delete_time"`
+	IndexingDeleteTotal             *int    `json:"indexing.delete_total,string"`
+	IndexingIndexCurrent            *int    `json:"indexing.index_current,string"`
+	IndexingIndexTime               *string `json:"indexing.index_time"`
+	IndexingIndexTotal              *int    `json:"indexing.index_total,string"`
+	IndexingIndexFailed             *int    `json:"indexing.index_failed,string"`
+	MergesCurrent                   *int    `json:"merges.current,string"`
+	MergesCurrentDoc                *int    `json:"merges.current_docs,string"`
+	MergesCurrentSize               *string `json:"merges.current_size"`
+	MergesTotal                     *int    `json:"merges.total,string"`
+	MergesTotalDocs                 *int    `json:"merges.total_docs,string"`
+	MergesTotalSize                 *string `json:"merges.total_size"`
+	MergesTotalTime                 *string `json:"merges.total_time"`
+	RefreshTotal                    *int    `json:"refresh.total,string"`
+	RefreshTime                     *string `json:"refresh.time"`
+	RefreshExternalTotal            *int    `json:"refresh.external_total,string"`
+	RefreshExternalTime             *string `json:"refresh.external_time"`
+	RefreshListeners                *int    `json:"refresh.listeners,string"`
+	ScriptCompilations              *int    `json:"script.compilations,string"`
+	ScriptCacheEvictions            *int    `json:"script.cache_evictions,string"`
+	ScriptCompilationLimitTriggered *int    `json:"script.compilation_limit_triggered,string"`
+	SearchFetchCurrent              *int    `json:"search.fetch_current,string"`
+	SearchFetchTime                 *string `json:"search.fetch_time"`
+	SearchFetchTotal                *int    `json:"search.fetch_total,string"`
+	SearchOpenContexts              *int    `json:"search.open_contexts,string"`
+	SearchQueryCurrent              *int    `json:"search.query_current,string"`
+	SearchQueryTime                 *string `json:"search.query_time"`
+	SearchQueryTotal                *int    `json:"search.query_total,string"`
+	SearchConcurrentQueryCurrent    *int    `json:"search.concurrent_query_current,string"`
+	SearchConcurrentQueryTime       *string `json:"search.concurrent_query_time"`
+	SearchConcurrentQueryTotal      *int    `json:"search.concurrent_query_total,string"`
+	SearchConcurrentAvgSliceCount   *string `json:"search.concurrent_avg_slice_count"`
+	SearchScrollCurrent             *int    `json:"search.scroll_current,string"`
+	SearchScrollTime                *string `json:"search.scroll_time"`
+	SearchScrollTotal               *int    `json:"search.scroll_total,string"`
+	SearchPointInTimeCurrent        *int    `json:"search.point_in_time_current,string"`
+	SearchPointInTimeTime           *string `json:"search.point_in_time_time"`
+	SearchPointInTimeTotal          *int    `json:"search.point_in_time_total,string"`
+	SegmentsCount                   *int    `json:"segments.count,string"`
+	SegmentsMemory                  *string `json:"segments.memory"`
+	SegmentsIndexWriteMemory        *string `json:"segments.index_writer_memory"`
+	SegmentsVersionMapMemory        *string `json:"segments.version_map_memory"`
+	SegmentsFixedBitsetMemory       *string `json:"segments.fixed_bitset_memory"`
+	SuggestCurrent                  *int    `json:"suggest.current,string"`
+	SuggestTime                     *string `json:"suggest.time"`
+	SuggestTotal                    *int    `json:"suggest.total,string"`
 }
 
 // Inspect returns the Inspect type containing the raw *opensearch.Reponse
diff --git a/opensearchapi/api_cluster-stats.go b/opensearchapi/api_cluster-stats.go
index 38fd8d1..025aaba 100644
--- a/opensearchapi/api_cluster-stats.go
+++ b/opensearchapi/api_cluster-stats.go
@@ -46,9 +46,10 @@ func (r ClusterStatsReq) GetRequest() (*http.Request, error) {
 // ClusterStatsResp represents the returned struct of the ClusterStatsReq response
 type ClusterStatsResp struct {
 	NodesInfo struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresCause `json:"failures"`
 	} `json:"_nodes"`
 	ClusterName string              `json:"cluster_name"`
 	ClusterUUID string              `json:"cluster_uuid"`
diff --git a/opensearchapi/api_datastream-stats.go b/opensearchapi/api_datastream-stats.go
index 2596d70..0c6747a 100644
--- a/opensearchapi/api_datastream-stats.go
+++ b/opensearchapi/api_datastream-stats.go
@@ -49,6 +49,12 @@ type DataStreamStatsResp struct {
 		Total      int `json:"total"`
 		Successful int `json:"successful"`
 		Failed     int `json:"failed"`
+		Failures   []struct {
+			Shard  int           `json:"shard"`
+			Index  string        `json:"index"`
+			Status string        `json:"status"`
+			Reason FailuresCause `json:"reason"`
+		} `json:"failures"`
 	} `json:"_shards"`
 	DataStreamCount     int                      `json:"data_stream_count"`
 	BackingIndices      int                      `json:"backing_indices"`
diff --git a/opensearchapi/api_document-delete_by_query_rethrottle.go b/opensearchapi/api_document-delete_by_query_rethrottle.go
index 15efe22..4221107 100644
--- a/opensearchapi/api_document-delete_by_query_rethrottle.go
+++ b/opensearchapi/api_document-delete_by_query_rethrottle.go
@@ -81,7 +81,8 @@ type DocumentDeleteByQueryRethrottleResp struct {
 			} `json:"resource_stats"`
 		} `json:"tasks"`
 	} `json:"nodes"`
-	response *opensearch.Response
+	NodeFailures []FailuresCause `json:"node_failures"`
+	response     *opensearch.Response
 }
 
 // DocumentDeleteByQueryRethrottleResourceInfo is a sub type of DocumentDeleteByQueryRethrottleResp containing resource stats
diff --git a/opensearchapi/api_indices-block.go b/opensearchapi/api_indices-block.go
index 3d9513a..f023e40 100644
--- a/opensearchapi/api_indices-block.go
+++ b/opensearchapi/api_indices-block.go
@@ -46,8 +46,12 @@ type IndicesBlockResp struct {
 	Acknowledged       bool `json:"acknowledged"`
 	ShardsAcknowledged bool `json:"shards_acknowledged"`
 	Indices            []struct {
-		Name    string `json:"name"`
-		Blocked bool   `json:"blocked"`
+		Name         string `json:"name"`
+		Blocked      bool   `json:"blocked"`
+		FailedShards []struct {
+			ID       int             `json:"id"`
+			Failures []FailuresShard `json:"failures"`
+		} `json:"failed_shards"`
 	} `json:"indices"`
 	response *opensearch.Response
 }
diff --git a/opensearchapi/api_indices-cache.go b/opensearchapi/api_indices-cache.go
index e3e2bfa..7b4db97 100644
--- a/opensearchapi/api_indices-cache.go
+++ b/opensearchapi/api_indices-cache.go
@@ -45,9 +45,10 @@ func (r IndicesClearCacheReq) GetRequest() (*http.Request, error) {
 // IndicesClearCacheResp represents the returned struct of the index clear cache response
 type IndicesClearCacheResp struct {
 	Shards struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresShard `json:"failures"`
 	} `json:"_shards"`
 	response *opensearch.Response
 }
diff --git a/opensearchapi/api_indices-close.go b/opensearchapi/api_indices-close.go
index 99f00e4..9c51b8d 100644
--- a/opensearchapi/api_indices-close.go
+++ b/opensearchapi/api_indices-close.go
@@ -37,7 +37,10 @@ type IndicesCloseResp struct {
 	Acknowledged       bool `json:"acknowledged"`
 	ShardsAcknowledged bool `json:"shards_acknowledged"`
 	Indices            map[string]struct {
-		Closed bool `json:"closed"`
+		Closed       bool `json:"closed"`
+		FailedShards map[string]struct {
+			Failures []FailuresShard `json:"failures"`
+		} `json:"failed_shards"`
 	} `json:"indices"`
 	response *opensearch.Response
 }
diff --git a/opensearchapi/api_indices-flush.go b/opensearchapi/api_indices-flush.go
index 9ab38d3..966e830 100644
--- a/opensearchapi/api_indices-flush.go
+++ b/opensearchapi/api_indices-flush.go
@@ -44,9 +44,10 @@ func (r IndicesFlushReq) GetRequest() (*http.Request, error) {
 // IndicesFlushResp represents the returned struct of the flush indices response
 type IndicesFlushResp struct {
 	Shards struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresShard `json:"failures"`
 	} `json:"_shards"`
 	response *opensearch.Response
 }
diff --git a/opensearchapi/api_indices-forcemerge.go b/opensearchapi/api_indices-forcemerge.go
index 4ca388b..41bd2c9 100644
--- a/opensearchapi/api_indices-forcemerge.go
+++ b/opensearchapi/api_indices-forcemerge.go
@@ -44,9 +44,10 @@ func (r IndicesForcemergeReq) GetRequest() (*http.Request, error) {
 // IndicesForcemergeResp represents the returned struct of the flush indices response
 type IndicesForcemergeResp struct {
 	Shards struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresShard `json:"failures"`
 	} `json:"_shards"`
 	response *opensearch.Response
 }
diff --git a/opensearchapi/api_indices-segments.go b/opensearchapi/api_indices-segments.go
index 0e6a817..ca3fb64 100644
--- a/opensearchapi/api_indices-segments.go
+++ b/opensearchapi/api_indices-segments.go
@@ -44,9 +44,10 @@ func (r IndicesSegmentsReq) GetRequest() (*http.Request, error) {
 // IndicesSegmentsResp represents the returned struct of the index shrink response
 type IndicesSegmentsResp struct {
 	Shards struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresShard `json:"failures"`
 	} `json:"_shards"`
 	Indices map[string]struct {
 		Shards map[string][]IndicesSegmentsShards `json:"shards"`
diff --git a/opensearchapi/api_indices-shardstores.go b/opensearchapi/api_indices-shardstores.go
index e9584b8..c6946d2 100644
--- a/opensearchapi/api_indices-shardstores.go
+++ b/opensearchapi/api_indices-shardstores.go
@@ -49,6 +49,7 @@ type IndicesShardStoresResp struct {
 			Stores []json.RawMessage `json:"stores"`
 		} `json:"shards"`
 	} `json:"indices"`
+	Failures []FailuresShard `json:"failures"`
 	response *opensearch.Response
 }
 
diff --git a/opensearchapi/api_indices-stats.go b/opensearchapi/api_indices-stats.go
index 80e0877..fccc92c 100644
--- a/opensearchapi/api_indices-stats.go
+++ b/opensearchapi/api_indices-stats.go
@@ -63,9 +63,10 @@ func (r IndicesStatsResp) Inspect() Inspect {
 
 // IndicesStatsShards is a sub type of IndicesStatsResp containing information about how many shards got requested
 type IndicesStatsShards struct {
-	Total      int `json:"total"`
-	Successful int `json:"successful"`
-	Failed     int `json:"failed"`
+	Total      int             `json:"total"`
+	Successful int             `json:"successful"`
+	Failed     int             `json:"failed"`
+	Failures   []FailuresShard `json:"failures"`
 }
 
 // IndicesStatsDocs is a sub type of IndicesStatsInfo containing stats about the index documents
diff --git a/opensearchapi/api_indices-validate.go b/opensearchapi/api_indices-validate.go
index 7e1a3fa..742a640 100644
--- a/opensearchapi/api_indices-validate.go
+++ b/opensearchapi/api_indices-validate.go
@@ -47,9 +47,10 @@ func (r IndicesValidateQueryReq) GetRequest() (*http.Request, error) {
 // IndicesValidateQueryResp represents the returned struct of the index shrink response
 type IndicesValidateQueryResp struct {
 	Shards struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresShard `json:"failures"`
 	} `json:"_shards"`
 	Valid        bool    `json:"valid"`
 	Error        *string `json:"error"`
diff --git a/opensearchapi/api_nodes-info.go b/opensearchapi/api_nodes-info.go
index 559485b..6c518a0 100644
--- a/opensearchapi/api_nodes-info.go
+++ b/opensearchapi/api_nodes-info.go
@@ -54,9 +54,10 @@ func (r NodesInfoReq) GetRequest() (*http.Request, error) {
 // NodesInfoResp represents the returned struct of the /_nodes response
 type NodesInfoResp struct {
 	NodesInfo struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresCause `json:"failures"`
 	} `json:"_nodes"`
 	ClusterName string               `json:"cluster_name"`
 	Nodes       map[string]NodesInfo `json:"nodes"`
diff --git a/opensearchapi/api_nodes-reload_security.go b/opensearchapi/api_nodes-reload_security.go
index 3240909..8fda290 100644
--- a/opensearchapi/api_nodes-reload_security.go
+++ b/opensearchapi/api_nodes-reload_security.go
@@ -51,9 +51,10 @@ func (r NodesReloadSecurityReq) GetRequest() (*http.Request, error) {
 // NodesReloadSecurityResp represents the returned struct of the /_nodes response
 type NodesReloadSecurityResp struct {
 	NodesInfo struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresCause `json:"failures"`
 	} `json:"_nodes"`
 	ClusterName string `json:"cluster_name"`
 	Nodes       map[string]struct {
diff --git a/opensearchapi/api_nodes-stats.go b/opensearchapi/api_nodes-stats.go
index 3dd08ba..4e2e3e5 100644
--- a/opensearchapi/api_nodes-stats.go
+++ b/opensearchapi/api_nodes-stats.go
@@ -62,9 +62,10 @@ func (r NodesStatsReq) GetRequest() (*http.Request, error) {
 // NodesStatsResp represents the returned struct of the /_nodes response
 type NodesStatsResp struct {
 	NodesInfo struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresCause `json:"failures"`
 	} `json:"_nodes"`
 	ClusterName string                `json:"cluster_name"`
 	Nodes       map[string]NodesStats `json:"nodes"`
diff --git a/opensearchapi/api_nodes-usage.go b/opensearchapi/api_nodes-usage.go
index 4b5f392..a4b07b4 100644
--- a/opensearchapi/api_nodes-usage.go
+++ b/opensearchapi/api_nodes-usage.go
@@ -55,9 +55,10 @@ func (r NodesUsageReq) GetRequest() (*http.Request, error) {
 // NodesUsageResp represents the returned struct of the /_nodes response
 type NodesUsageResp struct {
 	NodesUsage struct {
-		Total      int `json:"total"`
-		Successful int `json:"successful"`
-		Failed     int `json:"failed"`
+		Total      int             `json:"total"`
+		Successful int             `json:"successful"`
+		Failed     int             `json:"failed"`
+		Failures   []FailuresCause `json:"failures"`
 	} `json:"_nodes"`
 	ClusterName string                `json:"cluster_name"`
 	Nodes       map[string]NodesUsage `json:"nodes"`
diff --git a/opensearchapi/api_reindex_rethrottle.go b/opensearchapi/api_reindex_rethrottle.go
index 64ac0bb..d2e73cb 100644
--- a/opensearchapi/api_reindex_rethrottle.go
+++ b/opensearchapi/api_reindex_rethrottle.go
@@ -107,7 +107,8 @@ type ReindexRethrottleResp struct {
 			} `json:"resource_stats"`
 		} `json:"tasks"`
 	} `json:"nodes"`
-	response *opensearch.Response
+	NodeFailures []FailuresCause `json:"node_failures"`
+	response     *opensearch.Response
 }
 
 // Inspect returns the Inspect type containing the raw *opensearch.Reponse
diff --git a/opensearchapi/api_tasks-cancel.go b/opensearchapi/api_tasks-cancel.go
index 7809315..ef19a5e 100644
--- a/opensearchapi/api_tasks-cancel.go
+++ b/opensearchapi/api_tasks-cancel.go
@@ -43,8 +43,9 @@ func (r TasksCancelReq) GetRequest() (*http.Request, error) {
 
 // TasksCancelResp represents the returned struct of the index create response
 type TasksCancelResp struct {
-	Nodes    map[string]TaskCancel `json:"nodes"`
-	response *opensearch.Response
+	Nodes        map[string]TaskCancel `json:"nodes"`
+	NodeFailures []FailuresCause       `json:"node_failures"`
+	response     *opensearch.Response
 }
 
 // Inspect returns the Inspect type containing the raw *opensearch.Reponse
diff --git a/opensearchapi/api_tasks-list.go b/opensearchapi/api_tasks-list.go
index 7d3ecaf..2c86fdf 100644
--- a/opensearchapi/api_tasks-list.go
+++ b/opensearchapi/api_tasks-list.go
@@ -31,9 +31,10 @@ func (r TasksListReq) GetRequest() (*http.Request, error) {
 
 // TasksListResp represents the returned struct of the index create response
 type TasksListResp struct {
-	Nodes    map[string]TasksListNodes `json:"nodes"`
-	Tasks    map[string]TasksListTask  `json:"tasks"` // tasks is returned when group_by is set to none or parents
-	response *opensearch.Response
+	Nodes        map[string]TasksListNodes `json:"nodes"`
+	Tasks        map[string]TasksListTask  `json:"tasks"` // tasks is returned when group_by is set to none or parents
+	NodeFailures []FailuresCause           `json:"node_failures"`
+	response     *opensearch.Response
 }
 
 // Inspect returns the Inspect type containing the raw *opensearch.Reponse
diff --git a/opensearchapi/api_update_by_query_rethrottle.go b/opensearchapi/api_update_by_query_rethrottle.go
index aeec7fc..8a882f3 100644
--- a/opensearchapi/api_update_by_query_rethrottle.go
+++ b/opensearchapi/api_update_by_query_rethrottle.go
@@ -107,7 +107,8 @@ type UpdateByQueryRethrottleResp struct {
 			} `json:"resource_stats"`
 		} `json:"tasks"`
 	} `json:"nodes"`
-	response *opensearch.Response
+	NodeFailures []FailuresCause `json:"node_failures"`
+	response     *opensearch.Response
 }
 
 // Inspect returns the Inspect type containing the raw *opensearch.Reponse
diff --git a/opensearchapi/opensearchapi.go b/opensearchapi/opensearchapi.go
index dabd887..6f3daed 100644
--- a/opensearchapi/opensearchapi.go
+++ b/opensearchapi/opensearchapi.go
@@ -141,10 +141,34 @@ type ResponseShards struct {
 
 // ResponseShardsFailure is a sub type of ReponseShards containing information about a failed shard
 type ResponseShardsFailure struct {
-	Shard  int `json:"shard"`
-	Index  any `json:"index"`
+	Shard  int    `json:"shard"`
+	Index  any    `json:"index"`
+	Node   string `json:"node"`
 	Reason struct {
 		Type   string `json:"type"`
 		Reason string `json:"reason"`
 	} `json:"reason"`
 }
+
+// FailuresCause contains information about failure cause
+type FailuresCause struct {
+	Type   string `json:"type"`
+	Reason string `json:"reason"`
+	NodeID string `json:"node_id"`
+	Cause  *struct {
+		Type   string `json:"type"`
+		Reason string `json:"reason"`
+		Cause  *struct {
+			Type   string  `json:"type"`
+			Reason *string `json:"reason"`
+		} `json:"caused_by,omitempty"`
+	} `json:"caused_by,omitempty"`
+}
+
+// FailuresShard contains information about shard failures
+type FailuresShard struct {
+	Shard  int           `json:"shard"`
+	Index  string        `json:"index"`
+	Status string        `json:"status"`
+	Reason FailuresCause `json:"reason"`
+}
diff --git a/opensearchtransport/discovery.go b/opensearchtransport/discovery.go
index 93ed61e..2e0bbec 100644
--- a/opensearchtransport/discovery.go
+++ b/opensearchtransport/discovery.go
@@ -142,6 +142,10 @@ func (c *Client) getNodesInfo() ([]nodeInfo, error) {
 	if err != nil {
 		return nil, err
 	}
+
+	if res.Body == nil {
+		return nil, fmt.Errorf("unexpected empty body")
+	}
 	defer res.Body.Close()
 
 	if res.StatusCode > http.StatusOK {
diff --git a/plugins/security/api.go b/plugins/security/api.go
new file mode 100644
index 0000000..77202e7
--- /dev/null
+++ b/plugins/security/api.go
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// Config represents the client configuration
+type Config struct {
+	Client opensearch.Config
+}
+
+// Client represents the security Client summarizing all API calls
+type Client struct {
+	Client         *opensearch.Client
+	Account        accountClient
+	ActionGroups   actiongroupsClient
+	Audit          auditClient
+	InternalUsers  internalusersClient
+	NodesDN        nodesdnClient
+	Roles          rolesClient
+	RolesMapping   rolesmappingClient
+	SecurityConfig securityconfigClient
+	SSL            sslClient
+	Tenants        tenantsClient
+}
+
+// clientInit inits the Client with all sub clients
+func clientInit(rootClient *opensearch.Client) *Client {
+	client := &Client{
+		Client: rootClient,
+	}
+	client.Account = accountClient{apiClient: client}
+	client.ActionGroups = actiongroupsClient{apiClient: client}
+	client.Audit = auditClient{apiClient: client}
+	client.InternalUsers = internalusersClient{apiClient: client}
+	client.NodesDN = nodesdnClient{apiClient: client}
+	client.Roles = rolesClient{apiClient: client}
+	client.RolesMapping = rolesmappingClient{apiClient: client}
+	client.SecurityConfig = securityconfigClient{apiClient: client}
+	client.SSL = sslClient{apiClient: client}
+	client.Tenants = tenantsClient{apiClient: client}
+	return client
+}
+
+// NewClient returns a security client
+func NewClient(config Config) (*Client, error) {
+	rootClient, err := opensearch.NewClient(config.Client)
+	if err != nil {
+		return nil, err
+	}
+
+	return clientInit(rootClient), nil
+}
+
+// do calls the opensearch.Client.Do() and checks the response for errors
+func (c *Client) do(ctx context.Context, req opensearch.Request, dataPointer any) (*opensearch.Response, error) {
+	resp, err := c.Client.Do(ctx, req, dataPointer)
+	if err != nil {
+		return nil, err
+	}
+
+	if resp.IsError() {
+		if dataPointer != nil {
+			return resp, opensearch.ParseError(resp)
+		} else {
+			return resp, fmt.Errorf("status: %s", resp.Status())
+		}
+	}
+
+	return resp, nil
+}
diff --git a/plugins/security/api_account-get.go b/plugins/security/api_account-get.go
new file mode 100644
index 0000000..52f6009
--- /dev/null
+++ b/plugins/security/api_account-get.go
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// AccountGetReq represents possible options for the account get request
+type AccountGetReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r AccountGetReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"GET",
+		"/_plugins/_security/api/account",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// AccountGetResp represents the returned struct of the account get response
+type AccountGetResp struct {
+	UserName            string          `json:"user_name"`
+	IsReserved          bool            `json:"is_reserved"`
+	IsHidden            bool            `json:"is_hidden"`
+	IsInternaluser      bool            `json:"is_internal_user"`
+	BackendRoles        []string        `json:"backend_roles"`
+	CustomAttributes    []string        `json:"custom_attribute_names"`
+	UserRequestedTenant *string         `json:"user_requested_tenant"`
+	Tennants            map[string]bool `json:"tenants"`
+	Roles               []string        `json:"roles"`
+	response            *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r AccountGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_account-put.go b/plugins/security/api_account-put.go
new file mode 100644
index 0000000..b38c16c
--- /dev/null
+++ b/plugins/security/api_account-put.go
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// AccountPutReq represents possible options for the account put request
+type AccountPutReq struct {
+	Body AccountPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r AccountPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		"/_plugins/_security/api/account",
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// AccountPutBody reperensts the request body for AccountPutReq
+type AccountPutBody struct {
+	CurrentPassword string `json:"current_password"`
+	Password        string `json:"password"`
+}
+
+// AccountPutResp represents the returned struct of the account put response
+type AccountPutResp struct {
+	Message  string `json:"message"`
+	Status   string `json:"status"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r AccountPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_account.go b/plugins/security/api_account.go
new file mode 100644
index 0000000..d1f62bd
--- /dev/null
+++ b/plugins/security/api_account.go
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type accountClient struct {
+	apiClient *Client
+}
+
+// Get executes a get account request with the optional AccountGetReq
+func (c accountClient) Get(ctx context.Context, req *AccountGetReq) (AccountGetResp, error) {
+	if req == nil {
+		req = &AccountGetReq{}
+	}
+
+	var (
+		data AccountGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put account request with the required AccountPutReq
+func (c accountClient) Put(ctx context.Context, req AccountPutReq) (AccountPutResp, error) {
+	var (
+		data AccountPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_actiongroups-delete.go b/plugins/security/api_actiongroups-delete.go
new file mode 100644
index 0000000..fc89b39
--- /dev/null
+++ b/plugins/security/api_actiongroups-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ActionGroupsDeleteReq represents possible options for the actiongroups delete request
+type ActionGroupsDeleteReq struct {
+	ActionGroup string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ActionGroupsDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/actiongroups/%s", r.ActionGroup),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ActionGroupsDeleteResp represents the returned struct of the actiongroups delete response
+type ActionGroupsDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ActionGroupsDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_actiongroups-get.go b/plugins/security/api_actiongroups-get.go
new file mode 100644
index 0000000..5b8fd07
--- /dev/null
+++ b/plugins/security/api_actiongroups-get.go
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ActionGroupsGetReq represents possible options for the actiongroups get request
+type ActionGroupsGetReq struct {
+	Header      http.Header
+	ActionGroup string
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ActionGroupsGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/actiongroups/") + len(r.ActionGroup))
+	path.WriteString("/_plugins/_security/api/actiongroups")
+	if len(r.ActionGroup) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.ActionGroup)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ActionGroupsGetResp represents the returned struct of the actiongroups get response
+type ActionGroupsGetResp struct {
+	Groups   map[string]ActionGroupsGet
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ActionGroupsGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// ActionGroupsGet is a sub type of ActionGroupsGetResp represeting information about an action group
+type ActionGroupsGet struct {
+	Reserved       bool     `json:"reserved"`
+	Hidden         bool     `json:"hidden"`
+	AllowedActions []string `json:"allowed_actions"`
+	Static         bool     `json:"static"`
+	Description    string   `json:"description,omitempty"`
+	Type           string   `json:"type,omitempty"`
+}
diff --git a/plugins/security/api_actiongroups-patch.go b/plugins/security/api_actiongroups-patch.go
new file mode 100644
index 0000000..225558e
--- /dev/null
+++ b/plugins/security/api_actiongroups-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ActionGroupsPatchReq represents possible options for the actiongroups patch request
+type ActionGroupsPatchReq struct {
+	ActionGroup string
+	Body        ActionGroupsPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ActionGroupsPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/actiongroups/") + len(r.ActionGroup))
+	path.WriteString("/_plugins/_security/api/actiongroups")
+	if len(r.ActionGroup) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.ActionGroup)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ActionGroupsPatchResp represents the returned struct of the actiongroups patch response
+type ActionGroupsPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ActionGroupsPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// ActionGroupsPatchBody represents the request body for the action groups patch request
+type ActionGroupsPatchBody []ActionGroupsPatchBodyItem
+
+// ActionGroupsPatchBodyItem is a sub type of ActionGroupsPatchBody represeting an action group
+type ActionGroupsPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_actiongroups-put.go b/plugins/security/api_actiongroups-put.go
new file mode 100644
index 0000000..9ff52cb
--- /dev/null
+++ b/plugins/security/api_actiongroups-put.go
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ActionGroupsPutReq represents possible options for the actiongroups put request
+type ActionGroupsPutReq struct {
+	ActionGroup string
+	Body        ActionGroupsPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ActionGroupsPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/actiongroups/%s", r.ActionGroup),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ActionGroupsPutResp represents the returned struct of the actiongroups put response
+type ActionGroupsPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ActionGroupsPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// ActionGroupsPutBody represents the request body for the action groups put request
+type ActionGroupsPutBody struct {
+	AllowedActions []string `json:"allowed_actions"`
+	Type           *string  `json:"type,omitempty"`
+	Description    *string  `json:"description,omitempty"`
+}
diff --git a/plugins/security/api_actiongroups.go b/plugins/security/api_actiongroups.go
new file mode 100644
index 0000000..a5c5755
--- /dev/null
+++ b/plugins/security/api_actiongroups.go
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type actiongroupsClient struct {
+	apiClient *Client
+}
+
+// Get executes a get actiongroups request with the optional ActionGroupsGetReq
+func (c actiongroupsClient) Get(ctx context.Context, req *ActionGroupsGetReq) (ActionGroupsGetResp, error) {
+	if req == nil {
+		req = &ActionGroupsGetReq{}
+	}
+
+	var (
+		data ActionGroupsGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put actiongroups request with the required ActionGroupsPutReq
+func (c actiongroupsClient) Put(ctx context.Context, req ActionGroupsPutReq) (ActionGroupsPutResp, error) {
+	var (
+		data ActionGroupsPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete actiongroups request with the required ActionGroupsDeleteReq
+func (c actiongroupsClient) Delete(ctx context.Context, req ActionGroupsDeleteReq) (ActionGroupsDeleteResp, error) {
+	var (
+		data ActionGroupsDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch actiongroups request with the required ActionGroupsPatchReq
+func (c actiongroupsClient) Patch(ctx context.Context, req ActionGroupsPatchReq) (ActionGroupsPatchResp, error) {
+	var (
+		data ActionGroupsPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_audit-get.go b/plugins/security/api_audit-get.go
new file mode 100644
index 0000000..eee6ffe
--- /dev/null
+++ b/plugins/security/api_audit-get.go
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// AuditGetReq represents possible options for the audit get request
+type AuditGetReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r AuditGetReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"GET",
+		"/_plugins/_security/api/audit",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// AuditGetResp represents the returned struct of the audit get response
+type AuditGetResp struct {
+	ReadOnly []string    `json:"_readonly"`
+	Config   AuditConfig `json:"config"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r AuditGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_audit-patch.go b/plugins/security/api_audit-patch.go
new file mode 100644
index 0000000..734f086
--- /dev/null
+++ b/plugins/security/api_audit-patch.go
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// AuditPatchReq represents possible options for the audit patch request
+type AuditPatchReq struct {
+	Body AuditPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r AuditPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		"/_plugins/_security/api/audit",
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// AuditPatchResp represents the returned struct of the audit patch response
+type AuditPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r AuditPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// AuditPatchBody represents the request body for the audit patch request
+type AuditPatchBody []AuditPatchBodyItem
+
+// AuditPatchBodyItem is a sub type of AuditPatchBody represeting patch item
+type AuditPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_audit-put.go b/plugins/security/api_audit-put.go
new file mode 100644
index 0000000..e1d327f
--- /dev/null
+++ b/plugins/security/api_audit-put.go
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// AuditPutReq represents possible options for the audit put request
+type AuditPutReq struct {
+	Body AuditPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r AuditPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		"/_plugins/_security/api/audit/config",
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// AuditPutBody is an alias of AuditConfig uses as request body
+type AuditPutBody AuditConfig
+
+// AuditPutResp represents the returned struct of the audit put response
+type AuditPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r AuditPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_audit.go b/plugins/security/api_audit.go
new file mode 100644
index 0000000..0909a60
--- /dev/null
+++ b/plugins/security/api_audit.go
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+	"encoding/json"
+)
+
+type auditClient struct {
+	apiClient *Client
+}
+
+// Get executes a get audit request with the optional AuditGetReq
+func (c auditClient) Get(ctx context.Context, req *AuditGetReq) (AuditGetResp, error) {
+	if req == nil {
+		req = &AuditGetReq{}
+	}
+
+	var (
+		data AuditGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put audit request with the required AuditPutReq
+func (c auditClient) Put(ctx context.Context, req AuditPutReq) (AuditPutResp, error) {
+	var (
+		data AuditPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch audit request with the required AuditPatchReq
+func (c auditClient) Patch(ctx context.Context, req AuditPatchReq) (AuditPatchResp, error) {
+	var (
+		data AuditPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// AuditConfig represents the security audit config uses for Put and Get request
+type AuditConfig struct {
+	Compliance struct {
+		Enabled             bool            `json:"enabled"`
+		WriteLogDiffs       bool            `json:"write_log_diffs"`
+		ReadWatchedFields   json.RawMessage `json:"read_watched_fields"`
+		ReadIgnoreUsers     []string        `json:"read_ignore_users"`
+		WriteWatchedIndices []string        `json:"write_watched_indices"`
+		WriteIgnoreUsers    []string        `json:"write_ignore_users"`
+		ReadMetadataOnly    bool            `json:"read_metadata_only"`
+		WriteMetadataOnly   bool            `json:"write_metadata_only"`
+		ExternalConfig      bool            `json:"external_config"`
+		InternalConfig      bool            `json:"internal_config"`
+	} `json:"compliance"`
+	Enabled bool `json:"enabled"`
+	Audit   struct {
+		IgnoreUsers    []string `json:"ignore_users"`
+		IgnoreRequests []string `json:"ignore_requests"`
+		// Needs to be a pointer so the omitempty machtes on nil and not on empty slice
+		IgnoreHeaders               *[]string `json:"ignore_headers,omitempty"`
+		IgnoreURLParams             *[]string `json:"ignore_url_params,omitempty"`
+		DisabledRestCategories      []string  `json:"disabled_rest_categories"`
+		DisabledTransportCategories []string  `json:"disabled_transport_categories"`
+		LogRequestBody              bool      `json:"log_request_body"`
+		ResolveIndices              bool      `json:"resolve_indices"`
+		ResolveBulkRequests         bool      `json:"resolve_bulk_requests"`
+		ExcludeSensitiveHeaders     bool      `json:"exclude_sensitive_headers"`
+		EnableTransport             bool      `json:"enable_transport"`
+		EnableRest                  bool      `json:"enable_rest"`
+	} `json:"audit"`
+}
diff --git a/plugins/security/api_flushcache.go b/plugins/security/api_flushcache.go
new file mode 100644
index 0000000..05b1e1e
--- /dev/null
+++ b/plugins/security/api_flushcache.go
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// FlushCache executes a flush cache request with the optional FlushCacheReq
+func (c Client) FlushCache(ctx context.Context, req *FlushCacheReq) (FlushCacheResp, error) {
+	if req == nil {
+		req = &FlushCacheReq{}
+	}
+
+	var (
+		data FlushCacheResp
+		err  error
+	)
+	if data.response, err = c.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// FlushCacheReq represents possible options for the clush cache request
+type FlushCacheReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r FlushCacheReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		"/_plugins/_security/api/cache",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// FlushCacheResp represents the returned struct of the flush cache response
+type FlushCacheResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r FlushCacheResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_health.go b/plugins/security/api_health.go
new file mode 100644
index 0000000..68d4c53
--- /dev/null
+++ b/plugins/security/api_health.go
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// Health executes a get health request with the optional HealthReq
+func (c Client) Health(ctx context.Context, req *HealthReq) (HealthResp, error) {
+	if req == nil {
+		req = &HealthReq{}
+	}
+
+	var (
+		data HealthResp
+		err  error
+	)
+	if data.response, err = c.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// HealthReq represents possible options for the health get request
+type HealthReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r HealthReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"GET",
+		"/_plugins/_security/health",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// HealthResp represents the returned struct of the health get response
+type HealthResp struct {
+	Message  *string `json:"message"`
+	Mode     string  `json:"mode"`
+	Status   string  `json:"status"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r HealthResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_internalusers-delete.go b/plugins/security/api_internalusers-delete.go
new file mode 100644
index 0000000..36a13b9
--- /dev/null
+++ b/plugins/security/api_internalusers-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// InternalUsersDeleteReq represents possible options for the internalusers delete request
+type InternalUsersDeleteReq struct {
+	User string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r InternalUsersDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/internalusers/%s", r.User),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// InternalUsersDeleteResp represents the returned struct of the internalusers delete response
+type InternalUsersDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r InternalUsersDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_internalusers-get.go b/plugins/security/api_internalusers-get.go
new file mode 100644
index 0000000..e93dbf9
--- /dev/null
+++ b/plugins/security/api_internalusers-get.go
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// InternalUsersGetReq represents possible options for the internal users get request
+type InternalUsersGetReq struct {
+	User string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r InternalUsersGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/internalusers/") + len(r.User))
+	path.WriteString("/_plugins/_security/api/internalusers")
+	if len(r.User) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.User)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// InternalUsersGetResp represents the returned struct of the internal users get response
+type InternalUsersGetResp struct {
+	Users    map[string]InternalUsersGetItem
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r InternalUsersGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// InternalUsersGetItem is a sub type of InternalUsersGetResp containing information about a user
+type InternalUsersGetItem struct {
+	Hash          string            `json:"hash"`
+	Reserved      bool              `json:"reserved"`
+	Hidden        bool              `json:"hidden"`
+	BackendRoles  []string          `json:"backend_roles"`
+	Attributes    map[string]string `json:"attributes"`
+	Description   string            `json:"description"`
+	SecurityRoles []string          `json:"opendistro_security_roles"`
+	Statis        bool              `json:"static"`
+}
diff --git a/plugins/security/api_internalusers-patch.go b/plugins/security/api_internalusers-patch.go
new file mode 100644
index 0000000..79c8a9e
--- /dev/null
+++ b/plugins/security/api_internalusers-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// InternalUsersPatchReq represents possible options for the internalusers patch request
+type InternalUsersPatchReq struct {
+	User string
+	Body InternalUsersPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r InternalUsersPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/internalusers/") + len(r.User))
+	path.WriteString("/_plugins/_security/api/internalusers")
+	if len(r.User) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.User)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// InternalUsersPatchResp represents the returned struct of the internalusers patch response
+type InternalUsersPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r InternalUsersPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// InternalUsersPatchBody represents the request body for the internalusers patch request
+type InternalUsersPatchBody []InternalUsersPatchBodyItem
+
+// InternalUsersPatchBodyItem is a sub type of InternalUsersPatchBody represeting patch item
+type InternalUsersPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_internalusers-put.go b/plugins/security/api_internalusers-put.go
new file mode 100644
index 0000000..18e6635
--- /dev/null
+++ b/plugins/security/api_internalusers-put.go
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// InternalUsersPutReq represents possible options for the internalusers put request
+type InternalUsersPutReq struct {
+	User string
+	Body InternalUsersPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r InternalUsersPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/internalusers/%s", r.User),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// InternalUsersPutBody represents the request body for InternalUsersPutReq
+type InternalUsersPutBody struct {
+	Password      string            `json:"password,omitempty"`
+	Hash          string            `json:"hash,omitempty"`
+	BackendRoles  []string          `json:"backend_roles,omitempty"`
+	Attributes    map[string]string `json:"attributes,omitempty"`
+	Description   string            `json:"description,omitempty"`
+	SecurityRoles []string          `json:"opendistro_security_roles,omitempty"`
+}
+
+// InternalUsersPutResp represents the returned struct of the internalusers put response
+type InternalUsersPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r InternalUsersPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_internalusers.go b/plugins/security/api_internalusers.go
new file mode 100644
index 0000000..88399be
--- /dev/null
+++ b/plugins/security/api_internalusers.go
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type internalusersClient struct {
+	apiClient *Client
+}
+
+// Get executes a get internalusers request with the optional InternalUsersGetReq
+func (c internalusersClient) Get(ctx context.Context, req *InternalUsersGetReq) (InternalUsersGetResp, error) {
+	if req == nil {
+		req = &InternalUsersGetReq{}
+	}
+
+	var (
+		data InternalUsersGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put internalusers request with the required InternalUsersPutReq
+func (c internalusersClient) Put(ctx context.Context, req InternalUsersPutReq) (InternalUsersPutResp, error) {
+	var (
+		data InternalUsersPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete internalusers request with the required InternalUsersDeleteReq
+func (c internalusersClient) Delete(ctx context.Context, req InternalUsersDeleteReq) (InternalUsersDeleteResp, error) {
+	var (
+		data InternalUsersDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch internalusers request with the required InternalUsersPatchReq
+func (c internalusersClient) Patch(ctx context.Context, req InternalUsersPatchReq) (InternalUsersPatchResp, error) {
+	var (
+		data InternalUsersPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_nodesdn-delete.go b/plugins/security/api_nodesdn-delete.go
new file mode 100644
index 0000000..ce73c3c
--- /dev/null
+++ b/plugins/security/api_nodesdn-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// NodesDNDeleteReq represents possible options for the nodesdn delete request
+type NodesDNDeleteReq struct {
+	Cluster string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r NodesDNDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/nodesdn/%s", r.Cluster),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// NodesDNDeleteResp represents the returned struct of the nodesdn delete response
+type NodesDNDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r NodesDNDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_nodesdn-get.go b/plugins/security/api_nodesdn-get.go
new file mode 100644
index 0000000..97a6b19
--- /dev/null
+++ b/plugins/security/api_nodesdn-get.go
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// NodesDNGetReq represents possible options for the nodes dn get request
+type NodesDNGetReq struct {
+	Cluster string
+	Header  http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r NodesDNGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/nodesdn/") + len(r.Cluster))
+	path.WriteString("/_plugins/_security/api/nodesdn")
+	if len(r.Cluster) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Cluster)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// NodesDNGetResp represents the returned struct of the nodes dn get response
+type NodesDNGetResp struct {
+	DistinguishedNames map[string]struct {
+		NodesDN []string `json:"nodes_dn"`
+	}
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r NodesDNGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_nodesdn-patch.go b/plugins/security/api_nodesdn-patch.go
new file mode 100644
index 0000000..30c0d50
--- /dev/null
+++ b/plugins/security/api_nodesdn-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// NodesDNPatchReq represents possible options for the nodesdn patch request
+type NodesDNPatchReq struct {
+	Cluster string
+	Body    NodesDNPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r NodesDNPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/nodesdn/") + len(r.Cluster))
+	path.WriteString("/_plugins/_security/api/nodesdn")
+	if len(r.Cluster) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Cluster)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// NodesDNPatchResp represents the returned struct of the nodesdn patch response
+type NodesDNPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r NodesDNPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// NodesDNPatchBody represents the request body for the nodesdn patch request
+type NodesDNPatchBody []NodesDNPatchBodyItem
+
+// NodesDNPatchBodyItem is a sub type of NodesDNPatchBody represeting patch item
+type NodesDNPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_nodesdn-put.go b/plugins/security/api_nodesdn-put.go
new file mode 100644
index 0000000..a7328bf
--- /dev/null
+++ b/plugins/security/api_nodesdn-put.go
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// NodesDNPutReq represents possible options for the nodesdn put request
+type NodesDNPutReq struct {
+	Cluster string
+	Body    NodesDNPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r NodesDNPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/nodesdn/%s", r.Cluster),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// NodesDNPutBody reperensts the request body for NodesDNPutReq
+type NodesDNPutBody struct {
+	NodesDN []string `json:"nodes_dn"`
+}
+
+// NodesDNPutResp represents the returned struct of the nodesdn put response
+type NodesDNPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r NodesDNPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_nodesdn.go b/plugins/security/api_nodesdn.go
new file mode 100644
index 0000000..c424a8c
--- /dev/null
+++ b/plugins/security/api_nodesdn.go
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type nodesdnClient struct {
+	apiClient *Client
+}
+
+// Get executes a get nodesdn request with the optional NodesDNGetReq
+func (c nodesdnClient) Get(ctx context.Context, req *NodesDNGetReq) (NodesDNGetResp, error) {
+	if req == nil {
+		req = &NodesDNGetReq{}
+	}
+
+	var (
+		data NodesDNGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data.DistinguishedNames); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put nodesdn request with the required NodesDNPutReq
+func (c nodesdnClient) Put(ctx context.Context, req NodesDNPutReq) (NodesDNPutResp, error) {
+	var (
+		data NodesDNPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete nodesdn request with the required NodesDNDeleteReq
+func (c nodesdnClient) Delete(ctx context.Context, req NodesDNDeleteReq) (NodesDNDeleteResp, error) {
+	var (
+		data NodesDNDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a put nodesdn request with the required NodesDNPatchReq
+func (c nodesdnClient) Patch(ctx context.Context, req NodesDNPatchReq) (NodesDNPatchResp, error) {
+	var (
+		data NodesDNPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_roles-delete.go b/plugins/security/api_roles-delete.go
new file mode 100644
index 0000000..59983bb
--- /dev/null
+++ b/plugins/security/api_roles-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesDeleteReq represents possible options for the roles delete request
+type RolesDeleteReq struct {
+	Role string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/roles/%s", r.Role),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesDeleteResp represents the returned struct of the roles delete response
+type RolesDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_roles-get.go b/plugins/security/api_roles-get.go
new file mode 100644
index 0000000..ae94756
--- /dev/null
+++ b/plugins/security/api_roles-get.go
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesGetReq represents possible options for the roles get request
+type RolesGetReq struct {
+	Role string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/roles/") + len(r.Role))
+	path.WriteString("/_plugins/_security/api/roles")
+	if len(r.Role) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Role)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesGetResp represents the returned struct of the roles get response
+type RolesGetResp struct {
+	Roles    map[string]RolesGetItem
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// RolesGetItem is a sub type of RolesGetResp containing information about a role
+type RolesGetItem struct {
+	Reserved           bool                    `json:"reserved"`
+	Hidden             bool                    `json:"hidden"`
+	Description        string                  `json:"description"`
+	ClusterPermissions []string                `json:"cluster_permissions"`
+	IndexPermissions   []RolesIndexPermission  `json:"index_permissions"`
+	TenantPermissions  []RolesTenantPermission `json:"tenant_permissions"`
+	Statis             bool                    `json:"static"`
+}
diff --git a/plugins/security/api_roles-patch.go b/plugins/security/api_roles-patch.go
new file mode 100644
index 0000000..ecfd214
--- /dev/null
+++ b/plugins/security/api_roles-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesPatchReq represents possible options for the roles patch request
+type RolesPatchReq struct {
+	Role string
+	Body RolesPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/roles/") + len(r.Role))
+	path.WriteString("/_plugins/_security/api/roles")
+	if len(r.Role) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Role)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesPatchResp represents the returned struct of the roles patch response
+type RolesPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// RolesPatchBody represents the request body for the roles patch request
+type RolesPatchBody []RolesPatchBodyItem
+
+// RolesPatchBodyItem is a sub type of RolesPatchBody represeting patch item
+type RolesPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_roles-put.go b/plugins/security/api_roles-put.go
new file mode 100644
index 0000000..92144cb
--- /dev/null
+++ b/plugins/security/api_roles-put.go
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesPutReq represents possible options for the roles put request
+type RolesPutReq struct {
+	Role string
+	Body RolesPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/roles/%s", r.Role),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesPutBody represents the request body for RolesPutReq
+type RolesPutBody struct {
+	Description        string                  `json:"description,omitempty"`
+	ClusterPermissions []string                `json:"cluster_permissions,omitempty"`
+	IndexPermissions   []RolesIndexPermission  `json:"index_permissions,omitempty"`
+	TenantPermissions  []RolesTenantPermission `json:"tenant_permissions,omitempty"`
+}
+
+// RolesPutResp represents the returned struct of the roles put response
+type RolesPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_roles.go b/plugins/security/api_roles.go
new file mode 100644
index 0000000..8f904e2
--- /dev/null
+++ b/plugins/security/api_roles.go
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type rolesClient struct {
+	apiClient *Client
+}
+
+// Get executes a get roles request with the optional RolesGetReq
+func (c rolesClient) Get(ctx context.Context, req *RolesGetReq) (RolesGetResp, error) {
+	if req == nil {
+		req = &RolesGetReq{}
+	}
+
+	var (
+		data RolesGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put roles request with the required RolesPutReq
+func (c rolesClient) Put(ctx context.Context, req RolesPutReq) (RolesPutResp, error) {
+	var (
+		data RolesPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete roles request with the required RolesDeleteReq
+func (c rolesClient) Delete(ctx context.Context, req RolesDeleteReq) (RolesDeleteResp, error) {
+	var (
+		data RolesDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch roles request with the required RolesPatchReq
+func (c rolesClient) Patch(ctx context.Context, req RolesPatchReq) (RolesPatchResp, error) {
+	var (
+		data RolesPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// RolesIndexPermission contains index permissions and is used for Get and Put requests
+type RolesIndexPermission struct {
+	IndexPatterns  []string `json:"index_patterns,omitempty"`
+	DLS            string   `json:"dls,omitempty"`
+	FLS            string   `json:"fls,omitempty"`
+	MaskedFields   []string `json:"masked_fields,omitempty"`
+	AllowedActions []string `json:"allowed_actions,omitempty"`
+}
+
+// RolesTenantPermission contains tenant permissions and is used for Get and Put requests
+type RolesTenantPermission struct {
+	TenantPatterns []string `json:"tenant_patterns,omitempty"`
+	AllowedActions []string `json:"allowed_actions,omitempty"`
+}
diff --git a/plugins/security/api_rolesmapping-delete.go b/plugins/security/api_rolesmapping-delete.go
new file mode 100644
index 0000000..3e74ca9
--- /dev/null
+++ b/plugins/security/api_rolesmapping-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesMappingDeleteReq represents possible options for the roles delete request
+type RolesMappingDeleteReq struct {
+	Role string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesMappingDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/rolesmapping/%s", r.Role),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesMappingDeleteResp represents the returned struct of the roles delete response
+type RolesMappingDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesMappingDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_rolesmapping-get.go b/plugins/security/api_rolesmapping-get.go
new file mode 100644
index 0000000..d50114e
--- /dev/null
+++ b/plugins/security/api_rolesmapping-get.go
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesMappingGetReq represents possible options for the rolesmapping get request
+type RolesMappingGetReq struct {
+	Role string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesMappingGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/rolesmapping/") + len(r.Role))
+	path.WriteString("/_plugins/_security/api/rolesmapping")
+	if len(r.Role) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Role)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesMappingGetResp represents the returned struct of the rolesmapping get response
+type RolesMappingGetResp struct {
+	RolesMapping map[string]RolesMappingGetItem
+	response     *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesMappingGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// RolesMappingGetItem is a sub type of RolesMappingGetResp containing information about a role
+type RolesMappingGetItem struct {
+	Reserved        bool     `json:"reserved"`
+	Hidden          bool     `json:"hidden"`
+	Hosts           []string `json:"hosts"`
+	Users           []string `json:"users"`
+	BackendRoles    []string `json:"backend_roles"`
+	AndBackendRoles []string `json:"and_backend_roles"`
+	Description     string   `json:"description"`
+}
diff --git a/plugins/security/api_rolesmapping-patch.go b/plugins/security/api_rolesmapping-patch.go
new file mode 100644
index 0000000..ba5a4d9
--- /dev/null
+++ b/plugins/security/api_rolesmapping-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesMappingPatchReq represents possible options for the rolesmapping patch request
+type RolesMappingPatchReq struct {
+	Role string
+	Body RolesMappingPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesMappingPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/rolesmapping/") + len(r.Role))
+	path.WriteString("/_plugins/_security/api/rolesmapping")
+	if len(r.Role) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Role)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesMappingPatchResp represents the returned struct of the rolesmapping patch response
+type RolesMappingPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesMappingPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// RolesMappingPatchBody represents the request body for the rolesmapping patch request
+type RolesMappingPatchBody []RolesMappingPatchBodyItem
+
+// RolesMappingPatchBodyItem is a sub type of RolesMappingPatchBody represeting patch item
+type RolesMappingPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_rolesmapping-put.go b/plugins/security/api_rolesmapping-put.go
new file mode 100644
index 0000000..c3ddf8e
--- /dev/null
+++ b/plugins/security/api_rolesmapping-put.go
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// RolesMappingPutReq represents possible options for the rolesmapping put request
+type RolesMappingPutReq struct {
+	Role string
+	Body RolesMappingPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r RolesMappingPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/rolesmapping/%s", r.Role),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// RolesMappingPutBody represents the request body for RolesMappingPutReq
+type RolesMappingPutBody struct {
+	Hosts           []string `json:"hosts,omitempty"`
+	Users           []string `json:"users,omitempty"`
+	BackendRoles    []string `json:"backend_roles,omitempty"`
+	AndBackendRoles []string `json:"and_backend_roles,omitempty"`
+	Description     string   `json:"description,omitempty"`
+}
+
+// RolesMappingPutResp represents the returned struct of the rolesmapping put response
+type RolesMappingPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r RolesMappingPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_rolesmapping.go b/plugins/security/api_rolesmapping.go
new file mode 100644
index 0000000..d3062a7
--- /dev/null
+++ b/plugins/security/api_rolesmapping.go
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type rolesmappingClient struct {
+	apiClient *Client
+}
+
+// Get executes a get roles request with the optional RolesMappingGetReq
+func (c rolesmappingClient) Get(ctx context.Context, req *RolesMappingGetReq) (RolesMappingGetResp, error) {
+	if req == nil {
+		req = &RolesMappingGetReq{}
+	}
+
+	var (
+		data RolesMappingGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put roles request with the required RolesMappingPutReq
+func (c rolesmappingClient) Put(ctx context.Context, req RolesMappingPutReq) (RolesMappingPutResp, error) {
+	var (
+		data RolesMappingPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete roles request with the required RolesMappingDeleteReq
+func (c rolesmappingClient) Delete(ctx context.Context, req RolesMappingDeleteReq) (RolesMappingDeleteResp, error) {
+	var (
+		data RolesMappingDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch roles request with the required RolesMappingPatchReq
+func (c rolesmappingClient) Patch(ctx context.Context, req RolesMappingPatchReq) (RolesMappingPatchResp, error) {
+	var (
+		data RolesMappingPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_securityconfig-get.go b/plugins/security/api_securityconfig-get.go
new file mode 100644
index 0000000..89c8f6f
--- /dev/null
+++ b/plugins/security/api_securityconfig-get.go
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ConfigGetReq represents possible options for the securityconfig get request
+type ConfigGetReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ConfigGetReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"GET",
+		"/_plugins/_security/api/securityconfig",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ConfigGetResp represents the returned struct of the securityconfig get response
+type ConfigGetResp struct {
+	Config struct {
+		Dynamic ConfigDynamic `json:"dynamic"`
+	} `json:"config"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ConfigGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// ConfigDynamic represents the opensearch security config and is sub type of ConfigGetResp and ConfigPutBody
+type ConfigDynamic struct {
+	FilteredAliasMode            string              `json:"filtered_alias_mode"`
+	DisableRestAuth              bool                `json:"disable_rest_auth"`
+	DisableIntertransportAuth    bool                `json:"disable_intertransport_auth"`
+	RespectRequestIndicesOptions bool                `json:"respect_request_indices_options"`
+	Kibana                       ConfigDynamicKibana `json:"kibana"`
+	HTTP                         ConfigDynamicHTTP   `json:"http"`
+	Authc                        ConfigDynamicAuthc  `json:"authc"`
+	Authz                        ConfigDynamicAuthz  `json:"authz"`
+	AuthFailureListeners         json.RawMessage     `json:"auth_failure_listeners"`
+	DoNotFailOnForbidden         bool                `json:"do_not_fail_on_forbidden"`
+	MultiRolespanEnabled         bool                `json:"multi_rolespan_enabled"`
+	HostsResolverMode            string              `json:"hosts_resolver_mode"`
+	DoNotFailOnForbiddenEmpty    bool                `json:"do_not_fail_on_forbidden_empty"`
+	OnBehalfOf                   *struct {
+		Enabled bool `json:"enabled"`
+	} `json:"on_behalf_of,omitempty"`
+}
+
+// ConfigDynamicKibana is a sub type of ConfigDynamic containing security settings for kibana
+type ConfigDynamicKibana struct {
+	MultitenancyEnabled  bool    `json:"multitenancy_enabled"`
+	PrivateTenantEnabled *bool   `json:"private_tenant_enabled,omitempty"`
+	DefaultTenant        *string `json:"default_tenant,omitempty"`
+	ServerUsername       string  `json:"server_username"`
+	Index                string  `json:"index"`
+}
+
+// ConfigDynamicHTTP is a sub type of ConfigDynamic containing security settings for HTTP
+type ConfigDynamicHTTP struct {
+	AnonymousAuthEnabled bool `json:"anonymous_auth_enabled"`
+	Xff                  struct {
+		Enabled         bool   `json:"enabled"`
+		InternalProxies string `json:"internalProxies"`
+		RemoteIPHeader  string `json:"remoteIpHeader"`
+	} `json:"xff"`
+}
+
+// ConfigDynamicAuthc is a sub type of ConfigDynamic containing security settings for Authc
+type ConfigDynamicAuthc struct {
+	JwtAuthDomain struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool   `json:"challenge"`
+			Type      string `json:"type"`
+			Config    struct {
+				SigningKey                   string `json:"signing_key"`
+				JwtHeader                    string `json:"jwt_header"`
+				JwtClockSkewToleranceSeconds int    `json:"jwt_clock_skew_tolerance_seconds"`
+			} `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authentication_backend"`
+		Description string `json:"description"`
+	} `json:"jwt_auth_domain"`
+	LDAP struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool            `json:"challenge"`
+			Type      string          `json:"type"`
+			Config    json.RawMessage `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string `json:"type"`
+			Config struct {
+				EnableSSL           bool     `json:"enable_ssl"`
+				EnableStartTLS      bool     `json:"enable_start_tls"`
+				EnableSSLClientAuth bool     `json:"enable_ssl_client_auth"`
+				VerifyHostnames     bool     `json:"verify_hostnames"`
+				Hosts               []string `json:"hosts"`
+				Userbase            string   `json:"userbase"`
+				Usersearch          string   `json:"usersearch"`
+			} `json:"config"`
+		} `json:"authentication_backend"`
+		Description string `json:"description"`
+	} `json:"ldap"`
+	BasicInternalAuthDomain struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool            `json:"challenge"`
+			Type      string          `json:"type"`
+			Config    json.RawMessage `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authentication_backend"`
+		Description string `json:"description"`
+	} `json:"basic_internal_auth_domain"`
+	ProxyAuthDomain struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool   `json:"challenge"`
+			Type      string `json:"type"`
+			Config    struct {
+				UserHeader  string `json:"user_header"`
+				RolesHeader string `json:"roles_header"`
+			} `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authentication_backend"`
+		Description string `json:"description"`
+	} `json:"proxy_auth_domain"`
+	ClientcertAuthDomain struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool   `json:"challenge"`
+			Type      string `json:"type"`
+			Config    struct {
+				UsernameAttribute string `json:"username_attribute"`
+			} `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authentication_backend"`
+		Description string `json:"description"`
+	} `json:"clientcert_auth_domain"`
+	KerberosAuthDomain struct {
+		HTTPEnabled       bool  `json:"http_enabled"`
+		TransportEnabled  *bool `json:"transport_enabled,omitempty"`
+		Order             int   `json:"order"`
+		HTTPAuthenticator struct {
+			Challenge bool   `json:"challenge"`
+			Type      string `json:"type"`
+			Config    struct {
+				KrbDebug                bool `json:"krb_debug"`
+				StripRealmFromPrincipal bool `json:"strip_realm_from_principal"`
+			} `json:"config"`
+		} `json:"http_authenticator"`
+		AuthenticationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authentication_backend"`
+	} `json:"kerberos_auth_domain"`
+}
+
+// ConfigDynamicAuthz is a sub type of ConfigDynamic containing security settings for Authz
+type ConfigDynamicAuthz struct {
+	RolesFromAnotherLdap struct {
+		HTTPEnabled          bool  `json:"http_enabled"`
+		TransportEnabled     *bool `json:"transport_enabled,omitempty"`
+		AuthorizationBackend struct {
+			Type   string          `json:"type"`
+			Config json.RawMessage `json:"config"`
+		} `json:"authorization_backend"`
+		Description string `json:"description"`
+	} `json:"roles_from_another_ldap"`
+	RolesFromMyldap struct {
+		HTTPEnabled          bool  `json:"http_enabled"`
+		TransportEnabled     *bool `json:"transport_enabled,omitempty"`
+		AuthorizationBackend struct {
+			Type   string `json:"type"`
+			Config struct {
+				EnableSsl           bool     `json:"enable_ssl"`
+				EnableStartTLS      bool     `json:"enable_start_tls"`
+				EnableSslClientAuth bool     `json:"enable_ssl_client_auth"`
+				VerifyHostnames     bool     `json:"verify_hostnames"`
+				Hosts               []string `json:"hosts"`
+				Rolebase            string   `json:"rolebase"`
+				Rolesearch          string   `json:"rolesearch"`
+				Userrolename        string   `json:"userrolename"`
+				Rolename            string   `json:"rolename"`
+				ResolveNestedRoles  bool     `json:"resolve_nested_roles"`
+				Userbase            string   `json:"userbase"`
+				Usersearch          string   `json:"usersearch"`
+			} `json:"config"`
+		} `json:"authorization_backend"`
+		Description string `json:"description"`
+	} `json:"roles_from_myldap"`
+}
diff --git a/plugins/security/api_securityconfig-patch.go b/plugins/security/api_securityconfig-patch.go
new file mode 100644
index 0000000..083ba14
--- /dev/null
+++ b/plugins/security/api_securityconfig-patch.go
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ConfigPatchReq represents possible options for the securityconfig patch request
+type ConfigPatchReq struct {
+	Body ConfigPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ConfigPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		"/_plugins/_security/api/securityconfig",
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ConfigPatchResp represents the returned struct of the securityconfig patch response
+type ConfigPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ConfigPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// ConfigPatchBody represents the request body for the securityconfig patch request
+type ConfigPatchBody []ConfigPatchBodyItem
+
+// ConfigPatchBodyItem is a sub type of ConfigPatchBody represeting patch item
+type ConfigPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_securityconfig-put.go b/plugins/security/api_securityconfig-put.go
new file mode 100644
index 0000000..9fd628c
--- /dev/null
+++ b/plugins/security/api_securityconfig-put.go
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// ConfigPutReq represents possible options for the securityconfig get request
+type ConfigPutReq struct {
+	Body ConfigPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r ConfigPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		"/_plugins/_security/api/securityconfig/config",
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// ConfigPutBody represents the request body for ConfigPutReq
+type ConfigPutBody struct {
+	Dynamic ConfigDynamic `json:"dynamic"`
+}
+
+// ConfigPutResp represents the returned struct of the securityconfig get response
+type ConfigPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r ConfigPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_securityconfig.go b/plugins/security/api_securityconfig.go
new file mode 100644
index 0000000..535e7b3
--- /dev/null
+++ b/plugins/security/api_securityconfig.go
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type securityconfigClient struct {
+	apiClient *Client
+}
+
+// Get executes a get securityconfig request with the optional ConfigGetReq
+func (c securityconfigClient) Get(ctx context.Context, req *ConfigGetReq) (ConfigGetResp, error) {
+	if req == nil {
+		req = &ConfigGetReq{}
+	}
+
+	var (
+		data ConfigGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put securityconfig request with the required ConfigPutReq
+func (c securityconfigClient) Put(ctx context.Context, req ConfigPutReq) (ConfigPutResp, error) {
+	var (
+		data ConfigPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch securityconfig request with the required ConfigPatchReq
+func (c securityconfigClient) Patch(ctx context.Context, req ConfigPatchReq) (ConfigPatchResp, error) {
+	var (
+		data ConfigPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_ssl-get.go b/plugins/security/api_ssl-get.go
new file mode 100644
index 0000000..d145f2d
--- /dev/null
+++ b/plugins/security/api_ssl-get.go
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// SSLGetReq represents possible options for the ssl/certs get request
+type SSLGetReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r SSLGetReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"GET",
+		"/_plugins/_security/api/ssl/certs",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// SSLGetResp represents the returned struct of the ssl/certs get response
+type SSLGetResp struct {
+	HTTPCerts     []SSLCertItem `json:"http_certificates_list"`
+	TransportCert []SSLCertItem `json:"transport_certificates_list"`
+	response      *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r SSLGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// SSLCertItem is a sub type of SSLGetResp containing information about a cert
+type SSLCertItem struct {
+	IssuerDN  string    `json:"issuer_dn"`
+	SubjectDN string    `json:"subject_dn"`
+	SAN       string    `json:"san"`
+	NotBefore time.Time `json:"not_before"`
+	NotAfter  time.Time `json:"not_after"`
+}
diff --git a/plugins/security/api_ssl-http-reload.go b/plugins/security/api_ssl-http-reload.go
new file mode 100644
index 0000000..1fb941e
--- /dev/null
+++ b/plugins/security/api_ssl-http-reload.go
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// SSLHTTPReloadReq represents possible options for the http ssl reload request
+type SSLHTTPReloadReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r SSLHTTPReloadReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"PUT",
+		"/_plugins/_security/api/ssl/http/reloadcerts",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// SSLHTTPReloadResp represents the returned struct of the http ssl reload response
+type SSLHTTPReloadResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r SSLHTTPReloadResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_ssl-transport-reload.go b/plugins/security/api_ssl-transport-reload.go
new file mode 100644
index 0000000..0c1c7e0
--- /dev/null
+++ b/plugins/security/api_ssl-transport-reload.go
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// SSLTransportReloadReq represents possible options for the transport ssl reload request
+type SSLTransportReloadReq struct {
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r SSLTransportReloadReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"PUT",
+		"/_plugins/_security/api/ssl/transport/reloadcerts",
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// SSLTransportReloadResp represents the returned struct of the transport ssl reload response
+type SSLTransportReloadResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r SSLTransportReloadResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_ssl.go b/plugins/security/api_ssl.go
new file mode 100644
index 0000000..d1ab08c
--- /dev/null
+++ b/plugins/security/api_ssl.go
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type sslClient struct {
+	apiClient *Client
+}
+
+// Get executes a get ssl request with the optional SSLGetReq
+func (c sslClient) Get(ctx context.Context, req *SSLGetReq) (SSLGetResp, error) {
+	if req == nil {
+		req = &SSLGetReq{}
+	}
+
+	var (
+		data SSLGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// HTTPReload executes a reload ssl request with the optional SSLHTTPReloadReq
+func (c sslClient) HTTPReload(ctx context.Context, req *SSLHTTPReloadReq) (SSLHTTPReloadResp, error) {
+	if req == nil {
+		req = &SSLHTTPReloadReq{}
+	}
+
+	var (
+		data SSLHTTPReloadResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// TransportReload executes a reload ssl request with the optional SSLTransportReloadReq
+func (c sslClient) TransportReload(ctx context.Context, req *SSLTransportReloadReq) (SSLTransportReloadResp, error) {
+	if req == nil {
+		req = &SSLTransportReloadReq{}
+	}
+
+	var (
+		data SSLTransportReloadResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/api_tenants-delete.go b/plugins/security/api_tenants-delete.go
new file mode 100644
index 0000000..6236136
--- /dev/null
+++ b/plugins/security/api_tenants-delete.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// TenantsDeleteReq represents possible options for the tenants delete request
+type TenantsDeleteReq struct {
+	Tenant string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r TenantsDeleteReq) GetRequest() (*http.Request, error) {
+	return opensearch.BuildRequest(
+		"DELETE",
+		fmt.Sprintf("/_plugins/_security/api/tenants/%s", r.Tenant),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// TenantsDeleteResp represents the returned struct of the tenants delete response
+type TenantsDeleteResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r TenantsDeleteResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_tenants-get.go b/plugins/security/api_tenants-get.go
new file mode 100644
index 0000000..5c1d30e
--- /dev/null
+++ b/plugins/security/api_tenants-get.go
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// TenantsGetReq represents possible options for the tenants get request
+type TenantsGetReq struct {
+	Tenant string
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r TenantsGetReq) GetRequest() (*http.Request, error) {
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/tenants/") + len(r.Tenant))
+	path.WriteString("/_plugins/_security/api/tenants")
+	if len(r.Tenant) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Tenant)
+	}
+
+	return opensearch.BuildRequest(
+		"GET",
+		path.String(),
+		nil,
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// TenantsGetResp represents the returned struct of the tenants get response
+type TenantsGetResp struct {
+	Tenants  map[string]TenantsGetItem
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r TenantsGetResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// TenantsGetItem is a sub type of TenantsGetResp containing information about a tenant
+type TenantsGetItem struct {
+	Reserved    bool   `json:"reserved"`
+	Hidden      bool   `json:"hidden"`
+	Description string `json:"description"`
+	Statis      bool   `json:"static"`
+}
diff --git a/plugins/security/api_tenants-patch.go b/plugins/security/api_tenants-patch.go
new file mode 100644
index 0000000..7285264
--- /dev/null
+++ b/plugins/security/api_tenants-patch.go
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// TenantsPatchReq represents possible options for the tenants patch request
+type TenantsPatchReq struct {
+	Tenant string
+	Body   TenantsPatchBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r TenantsPatchReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var path strings.Builder
+	path.Grow(len("/_plugins/_security/api/tenants/") + len(r.Tenant))
+	path.WriteString("/_plugins/_security/api/tenants")
+	if len(r.Tenant) > 0 {
+		path.WriteString("/")
+		path.WriteString(r.Tenant)
+	}
+
+	return opensearch.BuildRequest(
+		"PATCH",
+		path.String(),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// TenantsPatchResp represents the returned struct of the tenants patch response
+type TenantsPatchResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r TenantsPatchResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
+
+// TenantsPatchBody represents the request body for the tenants patch request
+type TenantsPatchBody []TenantsPatchBodyItem
+
+// TenantsPatchBodyItem is a sub type of TenantsPatchBody represeting patch item
+type TenantsPatchBodyItem struct {
+	OP    string `json:"op"`
+	Path  string `json:"path"`
+	Value any    `json:"value,omitempty"`
+}
diff --git a/plugins/security/api_tenants-put.go b/plugins/security/api_tenants-put.go
new file mode 100644
index 0000000..5dd3af5
--- /dev/null
+++ b/plugins/security/api_tenants-put.go
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+)
+
+// TenantsPutReq represents possible options for the tenants put request
+type TenantsPutReq struct {
+	Tenant string
+	Body   TenantsPutBody
+
+	Header http.Header
+}
+
+// GetRequest returns the *http.Request that gets executed by the client
+func (r TenantsPutReq) GetRequest() (*http.Request, error) {
+	body, err := json.Marshal(r.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return opensearch.BuildRequest(
+		"PUT",
+		fmt.Sprintf("/_plugins/_security/api/tenants/%s", r.Tenant),
+		bytes.NewReader(body),
+		make(map[string]string),
+		r.Header,
+	)
+}
+
+// TenantsPutBody is the request body for the TenantsPutReq
+type TenantsPutBody struct {
+	Description string `json:"description,omitempty"`
+}
+
+// TenantsPutResp represents the returned struct of the tenants put response
+type TenantsPutResp struct {
+	Status   string `json:"status"`
+	Message  string `json:"message"`
+	response *opensearch.Response
+}
+
+// Inspect returns the Inspect type containing the raw *opensearch.Reponse
+func (r TenantsPutResp) Inspect() Inspect {
+	return Inspect{Response: r.response}
+}
diff --git a/plugins/security/api_tenants.go b/plugins/security/api_tenants.go
new file mode 100644
index 0000000..2067b95
--- /dev/null
+++ b/plugins/security/api_tenants.go
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import (
+	"context"
+)
+
+type tenantsClient struct {
+	apiClient *Client
+}
+
+// Get executes a get tenants request with the optional TenantsGetReq
+func (c tenantsClient) Get(ctx context.Context, req *TenantsGetReq) (TenantsGetResp, error) {
+	if req == nil {
+		req = &TenantsGetReq{}
+	}
+
+	var (
+		data TenantsGetResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Put executes a put tenants request with the required TenantsPutReq
+func (c tenantsClient) Put(ctx context.Context, req TenantsPutReq) (TenantsPutResp, error) {
+	var (
+		data TenantsPutResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Delete executes a delete tenants request with the required TenantsDeleteReq
+func (c tenantsClient) Delete(ctx context.Context, req TenantsDeleteReq) (TenantsDeleteResp, error) {
+	var (
+		data TenantsDeleteResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// Patch executes a patch tenants request with the required TenantsPatchReq
+func (c tenantsClient) Patch(ctx context.Context, req TenantsPatchReq) (TenantsPatchResp, error) {
+	var (
+		data TenantsPatchResp
+		err  error
+	)
+	if data.response, err = c.apiClient.do(ctx, req, &data); err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
diff --git a/plugins/security/inspect.go b/plugins/security/inspect.go
new file mode 100644
index 0000000..cf3dfad
--- /dev/null
+++ b/plugins/security/inspect.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package security
+
+import "github.com/opensearch-project/opensearch-go/v3"
+
+// Inspect represents the struct returned by Inspect() func, its main use is to return the opensearch.Response to the user
+type Inspect struct {
+	Response *opensearch.Response
+}


Test patch:
diff --git a/.github/workflows/test-compatibility.yml b/.github/workflows/test-compatibility.yml
index 7594228..50c4a3f 100644
--- a/.github/workflows/test-compatibility.yml
+++ b/.github/workflows/test-compatibility.yml
@@ -55,7 +55,7 @@ jobs:
         run: |
           export OPENSEARCH_VERSION=${{ matrix.entry.opensearch_version }}
           export SECURE_INTEGRATION=${{ matrix.secured }}
-          make test-integ race=true
+          make cluster.get-cert test-integ race=true
 
       - name: Stop the OpenSearch cluster
         run: |
diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml
index 7196ae3..2862b8f 100644
--- a/.github/workflows/test-integration.yml
+++ b/.github/workflows/test-integration.yml
@@ -26,11 +26,7 @@ jobs:
           for attempt in `seq 25`; do sleep 5; \
           if curl -s localhost:9200; \
           then echo '=====> ready'; break; fi; if [ $attempt == 25 ]; then exit 1; fi; echo '=====> waiting...'; done
-      - run: make test-integ race=true coverage=true
-      - uses: codecov/codecov-action@v3
-        with:
-          file: tmp/integ.cov
-          flags: integration
+      - run: make test-integ race=true
 
   secured:
     name: Tests against secure cluster
@@ -54,4 +50,8 @@ jobs:
           for attempt in `seq 25`; do sleep 5; \
           if curl -s -ku admin:myStrongPassword123! https://localhost:9200; \
           then echo '=====> ready'; break; fi; if [ $attempt == 25 ]; then exit 1; fi; echo '=====> waiting...'; done
-      - run: make test-integ-secure
+      - run: make cluster.get-cert test-integ-secure race=true coverage=true
+      - uses: codecov/codecov-action@v4
+        with:
+          file: tmp/integ.cov
+          flags: integration
diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml
index b5717ed..38f21ee 100644
--- a/.github/workflows/test-unit.yml
+++ b/.github/workflows/test-unit.yml
@@ -22,7 +22,7 @@ jobs:
         if: matrix.os != 'ubuntu-latest'
       - run: make test-unit race=true coverage=true
         if: matrix.os == 'ubuntu-latest'
-      - uses: codecov/codecov-action@v3
+      - uses: codecov/codecov-action@v4
         with:
           file: tmp/unit.cov
           flags: unit
diff --git a/opensearch_internal_test.go b/opensearch_internal_test.go
index 637dfa8..383b0ce 100644
--- a/opensearch_internal_test.go
+++ b/opensearch_internal_test.go
@@ -29,19 +29,24 @@
 package opensearch
 
 import (
+	"context"
 	"errors"
+	"fmt"
 	"io"
 	"net/http"
 	"net/url"
 	"os"
-	"regexp"
 	"strings"
 	"testing"
+	"testing/iotest"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/opensearch-project/opensearch-go/v3/opensearchtransport"
 )
 
-var called bool
+var called int
 
 type mockTransp struct {
 	RoundTripFunc func(*http.Request) (*http.Response, error)
@@ -58,8 +63,8 @@ var defaultRoundTripFunc = func(req *http.Request) (*http.Response, error) {
 		  }
 		}`))
 		response.Header.Add("Content-Type", "application/json")
-	} else {
-		called = true
+	} else if req.URL.Path == "/test" {
+		called++
 	}
 
 	return response, nil
@@ -72,31 +77,44 @@ func (t *mockTransp) RoundTrip(req *http.Request) (*http.Response, error) {
 	return t.RoundTripFunc(req)
 }
 
+type testReq struct {
+	Method  string
+	Path    string
+	Body    io.Reader
+	Params  map[string]string
+	Error   bool
+	Headers http.Header
+}
+
+func (r testReq) GetRequest() (*http.Request, error) {
+	if r.Error {
+		return nil, fmt.Errorf("test error")
+	}
+	if r.Method == "" {
+		r.Method = http.MethodGet
+	}
+	return BuildRequest(
+		r.Method,
+		r.Path,
+		r.Body,
+		r.Params,
+		r.Headers,
+	)
+}
+
 func TestClientConfiguration(t *testing.T) {
 	t.Run("With empty", func(t *testing.T) {
 		c, err := NewDefaultClient()
-		if err != nil {
-			t.Errorf("Unexpected error: %s", err)
-		}
-
+		require.NoError(t, err)
 		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
-
-		if u != defaultURL {
-			t.Errorf("Unexpected URL, want=%s, got=%s", defaultURL, u)
-		}
+		assert.Equal(t, u, defaultURL)
 	})
 
 	t.Run("With URL from Addresses", func(t *testing.T) {
 		c, err := NewClient(Config{Addresses: []string{"http://localhost:8080//"}, Transport: &mockTransp{}})
-		if err != nil {
-			t.Fatalf("Unexpected error: %s", err)
-		}
-
+		require.NoError(t, err)
 		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
-
-		if u != "http://localhost:8080" {
-			t.Errorf("Unexpected URL, want=http://localhost:8080, got=%s", u)
-		}
+		assert.Equal(t, u, "http://localhost:8080")
 	})
 
 	t.Run("With URL from OPENSEARCH_URL", func(t *testing.T) {
@@ -104,15 +122,9 @@ func TestClientConfiguration(t *testing.T) {
 		defer func() { os.Setenv(envOpenSearchURL, "") }()
 
 		c, err := NewClient(Config{Transport: &mockTransp{}})
-		if err != nil {
-			t.Errorf("Unexpected error: %s", err)
-		}
-
+		require.NoError(t, err)
 		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
-
-		if u != "http://opensearch.com" {
-			t.Errorf("Unexpected URL, want=http://opensearch.com, got=%s", u)
-		}
+		assert.Equal(t, u, "http://opensearch.com")
 	})
 
 	t.Run("With URL from environment and cfg.Addresses", func(t *testing.T) {
@@ -120,34 +132,24 @@ func TestClientConfiguration(t *testing.T) {
 		defer func() { os.Setenv(envOpenSearchURL, "") }()
 
 		c, err := NewClient(Config{Addresses: []string{"http://localhost:8080//"}, Transport: &mockTransp{}})
-		if err != nil {
-			t.Fatalf("Unexpected error: %s", err)
-		}
-
+		require.NoError(t, err)
 		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
-
-		if u != "http://localhost:8080" {
-			t.Errorf("Unexpected URL, want=http://localhost:8080, got=%s", u)
-		}
+		assert.Equal(t, u, "http://localhost:8080")
 	})
 
 	t.Run("With invalid URL", func(t *testing.T) {
 		u := ":foo"
 		_, err := NewClient(Config{Addresses: []string{u}})
 
-		if err == nil {
-			t.Errorf("Expected error for URL %q, got %v", u, err)
-		}
+		assert.Error(t, err)
 	})
 
 	t.Run("With invalid URL from environment", func(t *testing.T) {
 		os.Setenv(envOpenSearchURL, ":foobar")
 		defer func() { os.Setenv(envOpenSearchURL, "") }()
 
-		c, err := NewDefaultClient()
-		if err == nil {
-			t.Errorf("Expected error, got: %+v", c)
-		}
+		_, err := NewDefaultClient()
+		assert.Error(t, err)
 	})
 
 	t.Run("With skip check", func(t *testing.T) {
@@ -162,31 +164,119 @@ func TestClientConfiguration(t *testing.T) {
 					},
 				},
 			})
-		if err != nil {
-			t.Errorf("Unexpected error, got: %+v", err)
-		}
+		assert.NoError(t, err)
+	})
+
+	t.Run("With User:Password", func(t *testing.T) {
+		c, err := NewClient(
+			Config{
+				Addresses: []string{"http://admin:admin@localhost:8080//"},
+				Transport: &mockTransp{},
+			},
+		)
+		require.NoError(t, err)
+		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
+		assert.Equal(t, u, "http://admin:admin@localhost:8080")
+	})
+
+	t.Run("With DiscoverNodes on start", func(t *testing.T) {
+		c, err := NewClient(
+			Config{
+				Addresses:            []string{"http://localhost:8080//"},
+				Transport:            &mockTransp{},
+				DiscoverNodesOnStart: true,
+			},
+		)
+		require.NoError(t, err)
+		u := c.Transport.(*opensearchtransport.Client).URLs()[0].String()
+		assert.Equal(t, u, "http://localhost:8080")
+	})
+
+	t.Run("With failing creation", func(t *testing.T) {
+		_, err := NewClient(
+			Config{
+				Addresses: []string{"http://admin:admin@localhost:8080//"},
+				Transport: &mockTransp{},
+				CACert:    []byte{1},
+			},
+		)
+		assert.ErrorIs(t, err, ErrCreateTransport)
 	})
 }
 
-func TestClientInterface(t *testing.T) {
-	t.Run("Transport", func(t *testing.T) {
+func TestClientInterfe(t *testing.T) {
+	t.Run("Perform()", func(t *testing.T) {
 		c, err := NewClient(Config{Transport: &mockTransp{}})
-		if err != nil {
-			t.Fatalf("Unexpected error: %s", err)
-		}
+		require.NoError(t, err)
 
-		if called != false {
-			t.Errorf("Unexpected call to transport by client")
-		}
+		call := called
 
-		res, err := c.Perform(&http.Request{URL: &url.URL{}, Header: make(http.Header)}) // errcheck ignore
+		res, err := c.Perform(&http.Request{URL: &url.URL{Path: "/test"}, Header: make(http.Header)})
 		if err == nil && res != nil && res.Body != nil {
 			res.Body.Close()
 		}
 
-		if called != true {
-			t.Errorf("Expected client to call transport")
+		assert.True(t, called-1 == call, "Expected client to call transport")
+	})
+
+	t.Run("Do()", func(t *testing.T) {
+		c, err := NewClient(Config{Transport: &mockTransp{}})
+		require.NoError(t, err)
+
+		req := testReq{}
+		resp, err := c.Do(context.TODO(), req, nil)
+		assert.NoError(t, err)
+		assert.NotNil(t, resp)
+	})
+
+	t.Run("Do() GetRequest error", func(t *testing.T) {
+		c, err := NewClient(Config{Transport: &mockTransp{}})
+		require.NoError(t, err)
+
+		req := testReq{Error: true}
+		resp, err := c.Do(context.TODO(), req, nil)
+		assert.Error(t, err)
+		assert.Nil(t, resp)
+	})
+
+	t.Run("Do() Unmarshal error", func(t *testing.T) {
+		c, err := NewClient(Config{Transport: &mockTransp{}})
+		require.NoError(t, err)
+
+		type failStr struct {
+			Version int `json:"version"`
+		}
+		req := testReq{Path: "/"}
+		resp, err := c.Do(context.TODO(), req, &failStr{})
+		require.Error(t, err)
+		assert.ErrorIs(t, err, ErrJSONUnmarshalBody)
+		assert.NotNil(t, resp)
+	})
+
+	t.Run("Do() io read error", func(t *testing.T) {
+		c, err := NewClient(
+			Config{
+				Transport: &mockTransp{
+					RoundTripFunc: func(req *http.Request) (*http.Response, error) {
+						return &http.Response{
+								StatusCode: http.StatusOK,
+								Body:       io.NopCloser(iotest.ErrReader(errors.New("io reader test"))),
+							},
+							nil
+					},
+				},
+			},
+		)
+		require.NoError(t, err)
+
+		type failStr struct {
+			Version int `json:"version"`
 		}
+		req := testReq{}
+		resp, err := c.Do(context.TODO(), req, &failStr{})
+		require.Error(t, err)
+		assert.ErrorIs(t, err, ErrReadBody)
+		assert.NotNil(t, resp)
 	})
 }
 
@@ -238,51 +328,34 @@ func TestAddrsToURLs(t *testing.T) {
 			res, err := addrsToURLs(tc.addrs)
 
 			if tc.err != nil {
-				if err == nil {
-					t.Errorf("Expected error, got: %v", err)
-				}
-				match, _ := regexp.MatchString(tc.err.Error(), err.Error())
-				if !match {
-					t.Errorf("Expected err [%s] to match: %s", err.Error(), tc.err.Error())
-				}
+				require.Error(t, err)
+				assert.Contains(t, err.Error(), tc.err.Error())
 			}
 
 			for i := range tc.urls {
-				if res[i].Scheme != tc.urls[i].Scheme {
-					t.Errorf("%s: Unexpected scheme, want=%s, got=%s", tc.name, tc.urls[i].Scheme, res[i].Scheme)
-				}
+				assert.Equal(t, tc.urls[i].Scheme, res[i].Scheme, tc.name)
 			}
 			for i := range tc.urls {
-				if res[i].Host != tc.urls[i].Host {
-					t.Errorf("%s: Unexpected host, want=%s, got=%s", tc.name, tc.urls[i].Host, res[i].Host)
-				}
+				assert.Equal(t, tc.urls[i].Host, res[i].Host, tc.name)
 			}
 			for i := range tc.urls {
-				if res[i].Path != tc.urls[i].Path {
-					t.Errorf("%s: Unexpected path, want=%s, got=%s", tc.name, tc.urls[i].Path, res[i].Path)
-				}
+				assert.Equal(t, tc.urls[i].Path, res[i].Path, tc.name)
 			}
 		})
 	}
 }
 
 func TestVersion(t *testing.T) {
-	if Version == "" {
-		t.Error("Version is empty")
-	}
+	require.NotEmpty(t, Version)
 }
 
 func TestClientMetrics(t *testing.T) {
 	c, _ := NewClient(Config{EnableMetrics: true, Transport: &mockTransp{}})
 
 	m, err := c.Metrics()
-	if err != nil {
-		t.Fatalf("Unexpected error: %v", err)
-	}
+	require.Nil(t, err)
 
-	if m.Requests > 1 {
-		t.Errorf("Unexpected output: %s", m)
-	}
+	assert.LessOrEqual(t, m.Requests, 1, m)
 }
 
 func TestParseElasticsearchVersion(t *testing.T) {
@@ -329,83 +402,21 @@ func TestParseElasticsearchVersion(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, got1, got2, err := ParseVersion(tt.version)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("ParseVersion() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if got != tt.major {
-				t.Errorf("ParseVersion() got = %v, want %v", got, tt.major)
-			}
-			if got1 != tt.minor {
-				t.Errorf("ParseVersion() got1 = %v, want %v", got1, tt.minor)
-			}
-			if got2 != tt.patch {
-				t.Errorf("ParseVersion() got2 = %v, want %v", got2, tt.patch)
+			major, minor, patch, err := ParseVersion(tt.version)
+			if tt.wantErr {
+				require.Error(t, err)
+			} else {
+				require.NoError(t, err)
 			}
+			assert.Equal(t, major, tt.major)
+			assert.Equal(t, minor, tt.minor)
+			assert.Equal(t, patch, tt.patch)
 		})
 	}
 }
 
-func TestGenuineCheckInfo(t *testing.T) {
-	tests := []struct {
-		name    string
-		info    info
-		wantErr bool
-		err     error
-	}{
-		{
-			name: "Supported OpenSearch 1.0.0",
-			info: info{
-				Version: esVersion{
-					Number:       "1.0.0",
-					Distribution: openSearch,
-				},
-			},
-			wantErr: false,
-			err:     nil,
-		},
-		{
-			name: "Supported Elasticsearch 7.10.0",
-			info: info{
-				Version: esVersion{
-					Number:      "7.10.0",
-					BuildFlavor: "anything",
-				},
-			},
-			wantErr: false,
-			err:     nil,
-		},
-		{
-			name: "Unsupported Elasticsearch Version 6.15.1",
-			info: info{
-				Version: esVersion{
-					Number:      "6.15.1",
-					BuildFlavor: "default",
-				},
-				Tagline: "You Know, for Search",
-			},
-			wantErr: true,
-			err:     errors.New(unsupportedProduct),
-		},
-		{
-			name: "Elasticsearch oss",
-			info: info{
-				Version: esVersion{
-					Number:      "7.10.0",
-					BuildFlavor: "oss",
-				},
-				Tagline: "You Know, for Search",
-			},
-			wantErr: false,
-			err:     nil,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if err := checkCompatibleInfo(tt.info); (err != nil) != tt.wantErr && !errors.Is(err, tt.err) {
-				t.Errorf("checkCompatibleInfo() error = %v, wantErr %v", err, tt.wantErr)
-			}
-		})
-	}
+func TestToPointer(t *testing.T) {
+	testPointer := ToPointer(true)
+	assert.NotNil(t, testPointer)
+	assert.True(t, *testPointer)
 }
diff --git a/opensearchtransport/discovery_internal_test.go b/opensearchtransport/discovery_internal_test.go
index 0a27d5f..79373c3 100644
--- a/opensearchtransport/discovery_internal_test.go
+++ b/opensearchtransport/discovery_internal_test.go
@@ -40,6 +40,9 @@ import (
 	"reflect"
 	"testing"
 	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestDiscovery(t *testing.T) {
@@ -80,7 +83,6 @@ func TestDiscovery(t *testing.T) {
 		if err != nil {
 			t.Fatalf("ERROR: %s", err)
 		}
-		fmt.Printf("NodesInfo: %+v\n", nodes)
 
 		if len(nodes) != 4 {
 			t.Errorf("Unexpected number of nodes, want=4, got=%d", len(nodes))
@@ -108,6 +110,24 @@ func TestDiscovery(t *testing.T) {
 		}
 	})
 
+	t.Run("getNodesInfo() empty Body", func(t *testing.T) {
+		newRoundTripper := func() http.RoundTripper {
+			return &mockTransp{
+				RoundTripFunc: func(req *http.Request) (*http.Response, error) {
+					return &http.Response{Header: http.Header{}}, nil
+				},
+			}
+		}
+
+		u, _ := url.Parse("http://localhost:8080")
+		tp, err := New(Config{URLs: []*url.URL{u}, Transport: newRoundTripper()})
+		require.NoError(t, err)
+
+		_, err = tp.getNodesInfo()
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "unexpected empty body")
+	})
+
 	t.Run("DiscoverNodes()", func(t *testing.T) {
 		u, _ := url.Parse("http://" + srv.Addr)
 		tp, _ := New(Config{URLs: []*url.URL{u}})
diff --git a/plugins/security/api_account_test.go b/plugins/security/api_account_test.go
new file mode 100644
index 0000000..d336d38
--- /dev/null
+++ b/plugins/security/api_account_test.go
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestAccountClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	testUser := "testUser"
+	t.Cleanup(func() { client.InternalUsers.Delete(nil, security.InternalUsersDeleteReq{User: testUser}) })
+
+	type accountTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []accountTests
+	}{
+		{
+			Name: "Get",
+			Tests: []accountTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Account.Get(nil, nil)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Account.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Put",
+			Tests: []accountTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						var nilResp ossectest.Response
+						// Get new client config
+						config, err := ossectest.ClientConfig()
+						if err != nil {
+							return nilResp, err
+						}
+
+						// Set password to a "strong" password
+						config.Client.Password = "Str0ngP4ss123!"
+						config.Client.Username = testUser
+
+						// Create the test user
+						_, err = client.InternalUsers.Put(
+							nil,
+							security.InternalUsersPutReq{
+								User: config.Client.Username,
+								Body: security.InternalUsersPutBody{
+									Password: config.Client.Password,
+								},
+							},
+						)
+						if err != nil {
+							return nilResp, err
+						}
+
+						// Create a new client with the test user
+						usrClient, err := security.NewClient(*config)
+						if err != nil {
+							return nilResp, err
+						}
+
+						// Run the change password request we want to test
+						return usrClient.Account.Put(
+							nil,
+							security.AccountPutReq{
+								Body: security.AccountPutBody{
+									CurrentPassword: config.Client.Password,
+									Password:        "myStrongPassword123!",
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Account.Put(nil, security.AccountPutReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/plugins/security/api_actiongroups_test.go b/plugins/security/api_actiongroups_test.go
new file mode 100644
index 0000000..ac6a5a8
--- /dev/null
+++ b/plugins/security/api_actiongroups_test.go
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestActiongroupsClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	type actiongroupsTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []actiongroupsTests
+	}{
+		{
+			Name: "Get",
+			Tests: []actiongroupsTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.ActionGroups.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.ActionGroups.Get(nil, &security.ActionGroupsGetReq{ActionGroup: "write"})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.ActionGroups.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Put",
+			Tests: []actiongroupsTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.ActionGroups.Put(
+							nil,
+							security.ActionGroupsPutReq{
+								ActionGroup: "test",
+								Body: security.ActionGroupsPutBody{
+									AllowedActions: []string{"indices:data/read/msearch*", "indices:admin/mapping/put"},
+									Type:           opensearch.ToPointer("index"),
+									Description:    opensearch.ToPointer("Test"),
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.ActionGroups.Put(nil, security.ActionGroupsPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []actiongroupsTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.ActionGroups.Delete(nil, security.ActionGroupsDeleteReq{ActionGroup: "test"})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.ActionGroups.Delete(nil, security.ActionGroupsDeleteReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []actiongroupsTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.ActionGroups.Patch(
+							nil,
+							security.ActionGroupsPatchReq{
+								Body: security.ActionGroupsPatchBody{security.ActionGroupsPatchBodyItem{
+									OP:   "add",
+									Path: "/test",
+									Value: security.ActionGroupsPutBody{
+										AllowedActions: []string{"indices:data/read/msearch*", "indices:admin/mapping/put"},
+									},
+								},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.ActionGroups.Patch(nil, security.ActionGroupsPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.ActionGroups.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.Groups, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/api_audit_test.go b/plugins/security/api_audit_test.go
new file mode 100644
index 0000000..72adece
--- /dev/null
+++ b/plugins/security/api_audit_test.go
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestAuditClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	var getResp security.AuditGetResp
+
+	type auditTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []auditTests
+	}{
+		{
+			Name: "Get",
+			Tests: []auditTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						getResp, err := client.Audit.Get(nil, nil)
+						return getResp, err
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Audit.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Put",
+			Tests: []auditTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Audit.Put(
+							nil,
+							security.AuditPutReq{
+								Body: security.AuditPutBody{
+									Compliance: getResp.Config.Compliance,
+									Enabled:    getResp.Config.Enabled,
+									Audit:      getResp.Config.Audit,
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Audit.Put(nil, security.AuditPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []auditTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Audit.Patch(
+							nil,
+							security.AuditPatchReq{
+								Body: security.AuditPatchBody{
+									security.AuditPatchBodyItem{
+										OP:    "add",
+										Path:  "/config/enabled",
+										Value: true,
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Audit.Patch(nil, security.AuditPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/plugins/security/api_flushcache_test.go b/plugins/security/api_flushcache_test.go
new file mode 100644
index 0000000..0e23f77
--- /dev/null
+++ b/plugins/security/api_flushcache_test.go
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestFlushCache(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	t.Run("without request", func(t *testing.T) {
+		resp, err := client.FlushCache(nil, nil)
+		require.Nil(t, err)
+		assert.NotNil(t, resp)
+		ostest.CompareRawJSONwithParsedJSON(t, resp, resp.Inspect().Response)
+	})
+
+	t.Run("inspect", func(t *testing.T) {
+		res, err := failingClient.FlushCache(nil, nil)
+		assert.NotNil(t, err)
+		assert.NotNil(t, res)
+		ossectest.VerifyInspect(t, res.Inspect())
+	})
+}
diff --git a/plugins/security/api_health_test.go b/plugins/security/api_health_test.go
new file mode 100644
index 0000000..6613a2f
--- /dev/null
+++ b/plugins/security/api_health_test.go
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestHealthClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	type healthTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []healthTests
+	}{
+		{
+			Name: "Health",
+			Tests: []healthTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Health(nil, nil)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Health(nil, nil)
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/plugins/security/api_internalusers_test.go b/plugins/security/api_internalusers_test.go
new file mode 100644
index 0000000..941aa43
--- /dev/null
+++ b/plugins/security/api_internalusers_test.go
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestInternalUsersClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	testUser := "test_user"
+
+	type internalusersTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []internalusersTests
+	}{
+		{
+			Name: "Put",
+			Tests: []internalusersTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.InternalUsers.Put(
+							nil,
+							security.InternalUsersPutReq{
+								User: testUser,
+								Body: security.InternalUsersPutBody{
+									Password: "myStrongPassword123!",
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.InternalUsers.Put(nil, security.InternalUsersPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []internalusersTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.InternalUsers.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.InternalUsers.Get(nil, &security.InternalUsersGetReq{User: testUser})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.InternalUsers.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []internalusersTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.InternalUsers.Delete(nil, security.InternalUsersDeleteReq{User: testUser})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.InternalUsers.Delete(nil, security.InternalUsersDeleteReq{User: testUser})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []internalusersTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.InternalUsers.Patch(
+							nil,
+							security.InternalUsersPatchReq{
+								Body: security.InternalUsersPatchBody{
+									security.InternalUsersPatchBodyItem{
+										OP:   "add",
+										Path: "/test",
+										Value: security.InternalUsersPutBody{
+											Password: "myStrongPassword123!",
+										},
+									},
+									security.InternalUsersPatchBodyItem{
+										OP:   "remove",
+										Path: "/test",
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.InternalUsers.Patch(nil, security.InternalUsersPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.InternalUsers.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.Users, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/api_nodesdn_test.go b/plugins/security/api_nodesdn_test.go
new file mode 100644
index 0000000..b3662c1
--- /dev/null
+++ b/plugins/security/api_nodesdn_test.go
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"crypto/tls"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestNodesDNClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	config, err := ossectest.ClientConfig()
+	require.Nil(t, err)
+
+	clientTLSCert, err := tls.LoadX509KeyPair("../../admin.pem", "../../admin.key")
+	require.Nil(t, err)
+
+	config.Client.Transport = &http.Transport{
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true,
+			Certificates:       []tls.Certificate{clientTLSCert},
+		},
+	}
+
+	client, err := security.NewClient(*config)
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	type nodesdnTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []nodesdnTests
+	}{
+		{
+			Name: "Put",
+			Tests: []nodesdnTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.NodesDN.Put(
+							nil,
+							security.NodesDNPutReq{
+								Cluster: "test",
+								Body: security.NodesDNPutBody{
+									NodesDN: []string{"CN=test.example.com"},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.NodesDN.Put(nil, security.NodesDNPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []nodesdnTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.NodesDN.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.NodesDN.Get(nil, &security.NodesDNGetReq{Cluster: "test"})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.NodesDN.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []nodesdnTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.NodesDN.Delete(nil, security.NodesDNDeleteReq{Cluster: "test"})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.NodesDN.Delete(nil, security.NodesDNDeleteReq{Cluster: "test"})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []nodesdnTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.NodesDN.Patch(
+							nil,
+							security.NodesDNPatchReq{
+								Body: security.NodesDNPatchBody{
+									security.NodesDNPatchBodyItem{
+										OP:   "add",
+										Path: "/test",
+										Value: security.NodesDNPutBody{
+											NodesDN: []string{"CN=test.example.com"},
+										},
+									},
+									security.NodesDNPatchBodyItem{
+										OP:   "remove",
+										Path: "/test",
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.NodesDN.Patch(nil, security.NodesDNPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.NodesDN.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.DistinguishedNames, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/api_roles_test.go b/plugins/security/api_roles_test.go
new file mode 100644
index 0000000..3fe4628
--- /dev/null
+++ b/plugins/security/api_roles_test.go
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestRolesClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	testRole := "test_role"
+
+	type rolesTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []rolesTests
+	}{
+		{
+			Name: "Put",
+			Tests: []rolesTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Roles.Put(
+							nil,
+							security.RolesPutReq{
+								Role: testRole,
+								Body: security.RolesPutBody{
+									Description:        "Test",
+									ClusterPermissions: []string{"cluster_monitor"},
+									IndexPermissions:   []security.RolesIndexPermission{security.RolesIndexPermission{IndexPatterns: []string{"*"}, AllowedActions: []string{"indices_monitor"}}},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Roles.Put(nil, security.RolesPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []rolesTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Roles.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Roles.Get(nil, &security.RolesGetReq{Role: testRole})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Roles.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []rolesTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Roles.Delete(nil, security.RolesDeleteReq{Role: testRole})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Roles.Delete(nil, security.RolesDeleteReq{Role: testRole})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []rolesTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Roles.Patch(
+							nil,
+							security.RolesPatchReq{
+								Body: security.RolesPatchBody{
+									security.RolesPatchBodyItem{
+										OP:   "add",
+										Path: "/test",
+										Value: security.RolesPutBody{
+											Description:        "Test",
+											ClusterPermissions: []string{"cluster_monitor"},
+											IndexPermissions:   []security.RolesIndexPermission{security.RolesIndexPermission{IndexPatterns: []string{"*"}, AllowedActions: []string{"indices_monitor"}}},
+										},
+									},
+									security.RolesPatchBodyItem{
+										OP:   "remove",
+										Path: "/test",
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Roles.Patch(nil, security.RolesPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						if err != nil {
+							fmt.Println(err)
+						}
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.Roles.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.Roles, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/api_rolesmapping_test.go b/plugins/security/api_rolesmapping_test.go
new file mode 100644
index 0000000..1c895c4
--- /dev/null
+++ b/plugins/security/api_rolesmapping_test.go
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestRolesMappingClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	testRole := "test_role"
+	client.Roles.Put(
+		nil,
+		security.RolesPutReq{
+			Role: testRole,
+			Body: security.RolesPutBody{
+				Description:        "Test",
+				ClusterPermissions: []string{"cluster_monitor"},
+				IndexPermissions:   []security.RolesIndexPermission{security.RolesIndexPermission{IndexPatterns: []string{"*"}, AllowedActions: []string{"indices_monitor"}}},
+			},
+		},
+	)
+	t.Cleanup(func() {
+		client.Roles.Delete(nil, security.RolesDeleteReq{Role: testRole})
+	})
+
+	type rolesmappingTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []rolesmappingTests
+	}{
+		{
+			Name: "Put",
+			Tests: []rolesmappingTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.RolesMapping.Put(
+							nil,
+							security.RolesMappingPutReq{
+								Role: testRole,
+								Body: security.RolesMappingPutBody{
+									Description:  "Test",
+									Users:        []string{"test"},
+									BackendRoles: []string{"test"},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.RolesMapping.Put(nil, security.RolesMappingPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []rolesmappingTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.RolesMapping.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.RolesMapping.Get(nil, &security.RolesMappingGetReq{Role: testRole})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.RolesMapping.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []rolesmappingTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.RolesMapping.Delete(nil, security.RolesMappingDeleteReq{Role: testRole})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.RolesMapping.Delete(nil, security.RolesMappingDeleteReq{Role: testRole})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []rolesmappingTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.RolesMapping.Patch(
+							nil,
+							security.RolesMappingPatchReq{
+								Body: security.RolesMappingPatchBody{
+									security.RolesMappingPatchBodyItem{
+										OP:   "add",
+										Path: "/test",
+										Value: security.RolesMappingPutBody{
+											Description:  "Test",
+											Users:        []string{"test"},
+											BackendRoles: []string{"test"},
+										},
+									},
+									security.RolesMappingPatchBodyItem{
+										OP:   "remove",
+										Path: "/test",
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.RolesMapping.Patch(nil, security.RolesMappingPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						if err != nil {
+							fmt.Println(err)
+						}
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.RolesMapping.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.RolesMapping, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/api_securityconfig_test.go b/plugins/security/api_securityconfig_test.go
new file mode 100644
index 0000000..29a7450
--- /dev/null
+++ b/plugins/security/api_securityconfig_test.go
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"crypto/tls"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestSecurityConfigClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	config, err := ossectest.ClientConfig()
+	require.Nil(t, err)
+
+	clientTLSCert, err := tls.LoadX509KeyPair("../../admin.pem", "../../admin.key")
+	require.Nil(t, err)
+
+	config.Client.Transport = &http.Transport{
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true,
+			Certificates:       []tls.Certificate{clientTLSCert},
+		},
+	}
+
+	client, err := security.NewClient(*config)
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	var putBody security.ConfigDynamic
+
+	type securityconfigTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []securityconfigTests
+	}{
+		{
+			Name: "Get",
+			Tests: []securityconfigTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						resp, err := client.SecurityConfig.Get(nil, nil)
+						putBody = resp.Config.Dynamic
+						return resp, err
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SecurityConfig.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Put",
+			Tests: []securityconfigTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.SecurityConfig.Put(
+							nil,
+							security.ConfigPutReq{
+								Body: security.ConfigPutBody{Dynamic: putBody},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SecurityConfig.Put(nil, security.ConfigPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []securityconfigTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.SecurityConfig.Patch(
+							nil,
+							security.ConfigPatchReq{
+								Body: security.ConfigPatchBody{
+									security.ConfigPatchBodyItem{
+										OP:    "replace",
+										Path:  "/config/dynamic/authc/basic_internal_auth_domain/http_enabled",
+										Value: false,
+									},
+									security.ConfigPatchBodyItem{
+										OP:    "replace",
+										Path:  "/config/dynamic/authc/basic_internal_auth_domain/http_enabled",
+										Value: true,
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SecurityConfig.Patch(nil, security.ConfigPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/plugins/security/api_ssl_test.go b/plugins/security/api_ssl_test.go
new file mode 100644
index 0000000..07af43f
--- /dev/null
+++ b/plugins/security/api_ssl_test.go
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"crypto/tls"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestSSLClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	config, err := ossectest.ClientConfig()
+	require.Nil(t, err)
+
+	osAPIclient, err := ostest.NewClient()
+	require.Nil(t, err)
+
+	ostest.SkipIfBelowVersion(t, osAPIclient, 1, 3, "SSLClient")
+
+	clientTLSCert, err := tls.LoadX509KeyPair("../../admin.pem", "../../admin.key")
+	require.Nil(t, err)
+
+	config.Client.Transport = &http.Transport{
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true,
+			Certificates:       []tls.Certificate{clientTLSCert},
+		},
+	}
+
+	client, err := security.NewClient(*config)
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	type sslTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []sslTests
+	}{
+		{
+			Name: "HTTPReload",
+			Tests: []sslTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.SSL.HTTPReload(nil, nil)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SSL.HTTPReload(nil, &security.SSLHTTPReloadReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "TransportReload",
+			Tests: []sslTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.SSL.TransportReload(nil, nil)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SSL.TransportReload(nil, &security.SSLTransportReloadReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []sslTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.SSL.Get(nil, nil)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.SSL.Get(nil, nil)
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					if strings.HasSuffix(value.Name, "Reload") && strings.Contains(testCase.Name, "request") {
+						ostest.SkipIfBelowVersion(t, osAPIclient, 2, 7, value.Name)
+					}
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/plugins/security/api_tenants_test.go b/plugins/security/api_tenants_test.go
new file mode 100644
index 0000000..f4787a5
--- /dev/null
+++ b/plugins/security/api_tenants_test.go
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+//go:build integration
+
+package security_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+	ossectest "github.com/opensearch-project/opensearch-go/v3/plugins/security/internal/test"
+)
+
+func TestTenantsClient(t *testing.T) {
+	ostest.SkipIfNotSecure(t)
+	client, err := ossectest.NewClient()
+	require.Nil(t, err)
+
+	failingClient, err := ossectest.CreateFailingClient()
+	require.Nil(t, err)
+
+	testTenant := "test_tenant"
+
+	type tenantsTests struct {
+		Name    string
+		Results func() (ossectest.Response, error)
+	}
+
+	testCases := []struct {
+		Name  string
+		Tests []tenantsTests
+	}{
+		{
+			Name: "Put",
+			Tests: []tenantsTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Tenants.Put(
+							nil,
+							security.TenantsPutReq{
+								Tenant: testTenant,
+								Body: security.TenantsPutBody{
+									Description: "Test",
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Tenants.Put(nil, security.TenantsPutReq{})
+					},
+				},
+			},
+		},
+		{
+			Name: "Get",
+			Tests: []tenantsTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Tenants.Get(nil, nil)
+					},
+				},
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Tenants.Get(nil, &security.TenantsGetReq{Tenant: testTenant})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Tenants.Get(nil, nil)
+					},
+				},
+			},
+		},
+		{
+			Name: "Delete",
+			Tests: []tenantsTests{
+				{
+					Name: "without request",
+					Results: func() (ossectest.Response, error) {
+						return client.Tenants.Delete(nil, security.TenantsDeleteReq{Tenant: testTenant})
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Tenants.Delete(nil, security.TenantsDeleteReq{Tenant: testTenant})
+					},
+				},
+			},
+		},
+		{
+			Name: "Patch",
+			Tests: []tenantsTests{
+				{
+					Name: "with request",
+					Results: func() (ossectest.Response, error) {
+						return client.Tenants.Patch(
+							nil,
+							security.TenantsPatchReq{
+								Body: security.TenantsPatchBody{
+									security.TenantsPatchBodyItem{
+										OP:   "add",
+										Path: "/test",
+										Value: security.TenantsPutBody{
+											Description: "Test",
+										},
+									},
+									security.TenantsPatchBodyItem{
+										OP:   "remove",
+										Path: "/test",
+									},
+								},
+							},
+						)
+					},
+				},
+				{
+					Name: "inspect",
+					Results: func() (ossectest.Response, error) {
+						return failingClient.Tenants.Patch(nil, security.TenantsPatchReq{})
+					},
+				},
+			},
+		},
+	}
+	for _, value := range testCases {
+		t.Run(value.Name, func(t *testing.T) {
+			for _, testCase := range value.Tests {
+				t.Run(testCase.Name, func(t *testing.T) {
+					res, err := testCase.Results()
+					if testCase.Name == "inspect" {
+						assert.NotNil(t, err)
+						assert.NotNil(t, res)
+						ossectest.VerifyInspect(t, res.Inspect())
+					} else {
+						if err != nil {
+							fmt.Println(err)
+						}
+						require.Nil(t, err)
+						require.NotNil(t, res)
+						assert.NotNil(t, res.Inspect().Response)
+						if value.Name != "Get" {
+							ostest.CompareRawJSONwithParsedJSON(t, res, res.Inspect().Response)
+						}
+					}
+				})
+			}
+		})
+	}
+	t.Run("ValidateResponse", func(t *testing.T) {
+		t.Run("Get", func(t *testing.T) {
+			resp, err := client.Tenants.Get(nil, nil)
+			assert.Nil(t, err)
+			assert.NotNil(t, resp)
+			ostest.CompareRawJSONwithParsedJSON(t, resp.Tenants, resp.Inspect().Response)
+		})
+	})
+}
diff --git a/plugins/security/internal/test/helper.go b/plugins/security/internal/test/helper.go
new file mode 100644
index 0000000..7442a0b
--- /dev/null
+++ b/plugins/security/internal/test/helper.go
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+
+package ossectest
+
+import (
+	"crypto/tls"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/opensearch-project/opensearch-go/v3"
+	ostest "github.com/opensearch-project/opensearch-go/v3/internal/test"
+	"github.com/opensearch-project/opensearch-go/v3/plugins/security"
+)
+
+// Response is a dummy interface to run tests with Inspect()
+type Response interface {
+	Inspect() security.Inspect
+}
+
+// NewClient returns an opensearchapi.Client that is adjusted for the wanted test case
+func NewClient() (*security.Client, error) {
+	config, err := ClientConfig()
+	if err != nil {
+		return nil, err
+	}
+	if config == nil {
+		return nil, fmt.Errorf("failed to get config: requires secure opensearch")
+	}
+	return security.NewClient(*config)
+}
+
+// ClientConfig returns an opensearchapi.Config for secure opensearch
+func ClientConfig() (*security.Config, error) {
+	if ostest.IsSecure() {
+		password, err := ostest.GetPassword()
+		if err != nil {
+			return nil, err
+		}
+
+		return &security.Config{
+			Client: opensearch.Config{
+				Username:  "admin",
+				Password:  password,
+				Addresses: []string{"https://localhost:9200"},
+				Transport: &http.Transport{
+					TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+				},
+			},
+		}, nil
+	}
+	//nolint:nilnil // easier to test with nil rather then doing complex error handling for tests
+	return nil, nil
+}
+
+// CreateFailingClient returns an security.Client that always return 400 with an empty object as body
+func CreateFailingClient() (*security.Client, error) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.Body != nil {
+			defer r.Body.Close()
+		}
+		w.WriteHeader(http.StatusBadRequest)
+		io.Copy(w, strings.NewReader(`{"status": "error", "reason": "Test Failing Client Response"}`))
+	}))
+
+	return security.NewClient(security.Config{Client: opensearch.Config{Addresses: []string{ts.URL}}})
+}
+
+// VerifyInspect validates the returned security.Inspect type
+func VerifyInspect(t *testing.T, inspect security.Inspect) {
+	t.Helper()
+	assert.NotEmpty(t, inspect)
+	assert.Equal(t, http.StatusBadRequest, inspect.Response.StatusCode)
+	assert.NotEmpty(t, inspect.Response.Body)
+}