Repository: Azure/azure-sdk-for-go
Pull request #17887 (2022-05-11 18:49:09)

Sync eng/common directory with azure-sdk-tools for PR 3323
Sync eng/common directory with azure-sdk-tools for PR https://github.com/Azure/azure-sdk-tools/pull/3323  See [eng/common workflow](https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/README.md#workflow)


Patch:
diff --git a/sdk/azcore/CHANGELOG.md b/sdk/azcore/CHANGELOG.md
index 3519fa5b66..6e961fafde 100644
--- a/sdk/azcore/CHANGELOG.md
+++ b/sdk/azcore/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Release History
 
-## 0.24.0 (Unreleased)
+## 1.0.0 (2022-05-12)
 
 ### Features Added
 * Added interface `runtime.PollingHandler` to support custom poller implementations.
@@ -27,6 +27,8 @@
   * `Result()` will cache the terminal result or error but not transient errors, allowing for additional retries.
 
 ### Other Changes
+* Updated to latest `internal` module and absorbed breaking changes.
+  * Use `temporal.Resource` and deleted copy.
 * The internal poller implementation has been refactored.
   * The implementation in `internal/pollers/poller.go` has been merged into `runtime/poller.go` with some slight modification.
   * The internal poller types had their methods updated to conform to the `runtime.PollingHandler` interface.
diff --git a/sdk/azcore/arm/runtime/policy_bearer_token.go b/sdk/azcore/arm/runtime/policy_bearer_token.go
index f77d299005..10a3606e9d 100644
--- a/sdk/azcore/arm/runtime/policy_bearer_token.go
+++ b/sdk/azcore/arm/runtime/policy_bearer_token.go
@@ -14,6 +14,7 @@ import (
 	armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
 	azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/internal/temporal"
 )
 
 type acquiringResourceState struct {
@@ -35,9 +36,9 @@ func acquire(state acquiringResourceState) (newResource azcore.AccessToken, newE
 // BearerTokenPolicy authorizes requests with bearer tokens acquired from a TokenCredential.
 type BearerTokenPolicy struct {
 	// mainResource is the resource to be retreived using the tenant specified in the credential
-	mainResource *shared.ExpiringResource[azcore.AccessToken, acquiringResourceState]
+	mainResource *temporal.Resource[azcore.AccessToken, acquiringResourceState]
 	// auxResources are additional resources that are required for cross-tenant applications
-	auxResources map[string]*shared.ExpiringResource[azcore.AccessToken, acquiringResourceState]
+	auxResources map[string]*temporal.Resource[azcore.AccessToken, acquiringResourceState]
 	// the following fields are read-only
 	cred    azcore.TokenCredential
 	options armpolicy.BearerTokenOptions
@@ -53,7 +54,7 @@ func NewBearerTokenPolicy(cred azcore.TokenCredential, opts *armpolicy.BearerTok
 	p := &BearerTokenPolicy{
 		cred:         cred,
 		options:      *opts,
-		mainResource: shared.NewExpiringResource(acquire),
+		mainResource: temporal.NewResource(acquire),
 	}
 	return p
 }
@@ -64,7 +65,7 @@ func (b *BearerTokenPolicy) Do(req *azpolicy.Request) (*http.Response, error) {
 		ctx: req.Raw().Context(),
 		p:   b,
 	}
-	tk, err := b.mainResource.GetResource(as)
+	tk, err := b.mainResource.Get(as)
 	if err != nil {
 		return nil, err
 	}
@@ -72,7 +73,7 @@ func (b *BearerTokenPolicy) Do(req *azpolicy.Request) (*http.Response, error) {
 	auxTokens := []string{}
 	for tenant, er := range b.auxResources {
 		as.tenant = tenant
-		auxTk, err := er.GetResource(as)
+		auxTk, err := er.Get(as)
 		if err != nil {
 			return nil, err
 		}
diff --git a/sdk/azcore/go.mod b/sdk/azcore/go.mod
index a36a7e30e1..b8b29f18eb 100644
--- a/sdk/azcore/go.mod
+++ b/sdk/azcore/go.mod
@@ -1,16 +1,16 @@
 module github.com/Azure/azure-sdk-for-go/sdk/azcore
 
+go 1.18
+
 require (
-	github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1
+	github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0
 	github.com/stretchr/testify v1.7.0
-	golang.org/x/net v0.0.0-20210610132358-84b48f89b13b
+	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	golang.org/x/text v0.3.6 // indirect
+	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
 )
-
-go 1.18
diff --git a/sdk/azcore/go.sum b/sdk/azcore/go.sum
index bd6ab735b2..b035bd5c86 100644
--- a/sdk/azcore/go.sum
+++ b/sdk/azcore/go.sum
@@ -1,31 +1,17 @@
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1 h1:sLZ/Y+P/5RRtsXWylBjB5lkgixYfm0MQPiwrSX//JSo=
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/sdk/azcore/internal/log/log.go b/sdk/azcore/internal/log/log.go
new file mode 100644
index 0000000000..0684cb3173
--- /dev/null
+++ b/sdk/azcore/internal/log/log.go
@@ -0,0 +1,38 @@
+//go:build go1.18
+// +build go1.18
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+// This is an internal helper package to combine the complete logging APIs.
+package log
+
+import (
+	azlog "github.com/Azure/azure-sdk-for-go/sdk/azcore/log"
+	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
+)
+
+type Event = log.Event
+
+const (
+	EventRequest     = azlog.EventRequest
+	EventResponse    = azlog.EventResponse
+	EventRetryPolicy = azlog.EventRetryPolicy
+	EventLRO         = azlog.EventLRO
+)
+
+func Write(cls log.Event, msg string) {
+	log.Write(cls, msg)
+}
+
+func Writef(cls log.Event, format string, a ...interface{}) {
+	log.Writef(cls, format, a...)
+}
+
+func SetListener(lst func(Event, string)) {
+	log.SetListener(lst)
+}
+
+func Should(cls log.Event) bool {
+	return log.Should(cls)
+}
diff --git a/sdk/azcore/internal/pollers/async/async.go b/sdk/azcore/internal/pollers/async/async.go
index 31ad14a4c3..0194b8b011 100644
--- a/sdk/azcore/internal/pollers/async/async.go
+++ b/sdk/azcore/internal/pollers/async/async.go
@@ -13,9 +13,9 @@ import (
 	"net/http"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // see https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/async-api-reference.md
diff --git a/sdk/azcore/internal/pollers/body/body.go b/sdk/azcore/internal/pollers/body/body.go
index e2e465e15f..99e9f2f8d0 100644
--- a/sdk/azcore/internal/pollers/body/body.go
+++ b/sdk/azcore/internal/pollers/body/body.go
@@ -12,8 +12,8 @@ import (
 	"net/http"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // Kind is the identifier of this type in a resume token.
diff --git a/sdk/azcore/internal/pollers/loc/loc.go b/sdk/azcore/internal/pollers/loc/loc.go
index 527bde0318..56c2b90292 100644
--- a/sdk/azcore/internal/pollers/loc/loc.go
+++ b/sdk/azcore/internal/pollers/loc/loc.go
@@ -13,9 +13,9 @@ import (
 	"net/http"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // Kind is the identifier of this type in a resume token.
@@ -46,7 +46,7 @@ type Poller[T any] struct {
 
 	Type     string `json:"type"`
 	PollURL  string `json:"pollURL"`
-	CurState int    `json:"state"`
+	CurState string `json:"state"`
 }
 
 // New creates a new Poller from the provided initial response.
@@ -69,13 +69,12 @@ func New[T any](pl exported.Pipeline, resp *http.Response) (*Poller[T], error) {
 		resp:     resp,
 		Type:     kind,
 		PollURL:  locURL,
-		CurState: resp.StatusCode,
+		CurState: pollers.StatusInProgress,
 	}, nil
 }
 
 func (p *Poller[T]) Done() bool {
-	// anything other than a 202 indicates a terminal state
-	return p.CurState != http.StatusAccepted
+	return pollers.IsTerminalState(p.CurState)
 }
 
 func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
@@ -84,15 +83,22 @@ func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
 		if h := resp.Header.Get(shared.HeaderLocation); h != "" {
 			p.PollURL = h
 		}
-		p.CurState = resp.StatusCode
+		// if provisioning state is available, use that.  this is only
+		// for some ARM LRO scenarios (e.g. DELETE with a Location header)
+		// so if it's missing then use HTTP status code.
+		provState, _ := pollers.GetProvisioningState(resp)
 		p.resp = resp
-		if p.CurState == http.StatusAccepted {
-			return pollers.StatusInProgress, nil
-		} else if p.CurState > 199 && p.CurState < 300 {
+		if provState != "" {
+			p.CurState = provState
+		} else if resp.StatusCode == http.StatusAccepted {
+			p.CurState = pollers.StatusInProgress
+		} else if resp.StatusCode > 199 && resp.StatusCode < 300 {
 			// any 2xx other than a 202 indicates success
-			return pollers.StatusSucceeded, nil
+			p.CurState = pollers.StatusSucceeded
+		} else {
+			p.CurState = pollers.StatusFailed
 		}
-		return pollers.StatusFailed, nil
+		return p.CurState, nil
 	})
 	if err != nil {
 		return nil, err
@@ -101,5 +107,5 @@ func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
 }
 
 func (p *Poller[T]) Result(ctx context.Context, out *T) error {
-	return pollers.ResultHelper(p.resp, p.resp.StatusCode > 399, out)
+	return pollers.ResultHelper(p.resp, pollers.Failed(p.CurState), out)
 }
diff --git a/sdk/azcore/internal/pollers/op/op.go b/sdk/azcore/internal/pollers/op/op.go
index ef1f285e18..dd714e768c 100644
--- a/sdk/azcore/internal/pollers/op/op.go
+++ b/sdk/azcore/internal/pollers/op/op.go
@@ -13,9 +13,9 @@ import (
 	"net/http"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // Applicable returns true if the LRO is using Operation-Location.
diff --git a/sdk/azcore/internal/pollers/util.go b/sdk/azcore/internal/pollers/util.go
index 2442c075b6..17ab7dadc3 100644
--- a/sdk/azcore/internal/pollers/util.go
+++ b/sdk/azcore/internal/pollers/util.go
@@ -17,8 +17,8 @@ import (
 	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // the well-known set of LRO status/provisioning state values.
diff --git a/sdk/azcore/internal/shared/constants.go b/sdk/azcore/internal/shared/constants.go
index e994e9f8a2..1900edff7b 100644
--- a/sdk/azcore/internal/shared/constants.go
+++ b/sdk/azcore/internal/shared/constants.go
@@ -30,5 +30,5 @@ const (
 	Module = "azcore"
 
 	// Version is the semantic version (see http://semver.org) of this module.
-	Version = "v0.24.0"
+	Version = "v1.0.0"
 )
diff --git a/sdk/azcore/internal/shared/expiring_resource.go b/sdk/azcore/internal/shared/expiring_resource.go
deleted file mode 100644
index f6eae3f517..0000000000
--- a/sdk/azcore/internal/shared/expiring_resource.go
+++ /dev/null
@@ -1,111 +0,0 @@
-//go:build go1.18
-// +build go1.18
-
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package shared
-
-import (
-	"sync"
-	"time"
-)
-
-// AcquireResource abstracts a method for refreshing an expiring resource.
-type AcquireResource[T, U any] func(state U) (newResource T, newExpiration time.Time, err error)
-
-// ExpiringResource is a temporal resource (usually a credential) that requires periodic refreshing.
-type ExpiringResource[T, U any] struct {
-	// cond is used to synchronize access to the shared resource embodied by the remaining fields
-	cond *sync.Cond
-
-	// acquiring indicates that some thread/goroutine is in the process of acquiring/updating the resource
-	acquiring bool
-
-	// resource contains the value of the shared resource
-	resource T
-
-	// expiration indicates when the shared resource expires; it is 0 if the resource was never acquired
-	expiration time.Time
-
-	// lastAttempt indicates when a thread/goroutine last attempted to acquire/update the resource
-	lastAttempt time.Time
-
-	// acquireResource is the callback function that actually acquires the resource
-	acquireResource AcquireResource[T, U]
-}
-
-// NewExpiringResource creates a new ExpiringResource that uses the specified AcquireResource for refreshing.
-func NewExpiringResource[T, U any](ar AcquireResource[T, U]) *ExpiringResource[T, U] {
-	return &ExpiringResource[T, U]{cond: sync.NewCond(&sync.Mutex{}), acquireResource: ar}
-}
-
-// GetResource returns the underlying resource.
-// If the resource is fresh, no refresh is performed.
-func (er *ExpiringResource[T, U]) GetResource(state U) (T, error) {
-	// If the resource is expiring within this time window, update it eagerly.
-	// This allows other threads/goroutines to keep running by using the not-yet-expired
-	// resource value while one thread/goroutine updates the resource.
-	const window = 5 * time.Minute   // This example updates the resource 5 minutes prior to expiration
-	const backoff = 30 * time.Second // Minimum wait time between eager update attempts
-
-	now, acquire, expired, resource := time.Now(), false, false, er.resource
-	// acquire exclusive lock
-	er.cond.L.Lock()
-	for {
-		expired = er.expiration.IsZero() || er.expiration.Before(now)
-		if expired {
-			// The resource was never acquired or has expired
-			if !er.acquiring {
-				// If another thread/goroutine is not acquiring/updating the resource, this thread/goroutine will do it
-				er.acquiring, acquire = true, true
-				break
-			}
-			// Getting here means that this thread/goroutine will wait for the updated resource
-		} else if er.expiration.Add(-window).Before(now) {
-			// The resource is valid but is expiring within the time window
-			if !er.acquiring && er.lastAttempt.Add(backoff).Before(now) {
-				// If another thread/goroutine is not acquiring/renewing the resource, and none has attempted
-				// to do so within the last 30 seconds, this thread/goroutine will do it
-				er.acquiring, acquire = true, true
-				break
-			}
-			// This thread/goroutine will use the existing resource value while another updates it
-			resource = er.resource
-			break
-		} else {
-			// The resource is not close to expiring, this thread/goroutine should use its current value
-			resource = er.resource
-			break
-		}
-		// If we get here, wait for the new resource value to be acquired/updated
-		er.cond.Wait()
-	}
-	er.cond.L.Unlock() // Release the lock so no threads/goroutines are blocked
-
-	var err error
-	if acquire {
-		// This thread/goroutine has been selected to acquire/update the resource
-		var expiration time.Time
-		var newValue T
-		er.lastAttempt = now
-		newValue, expiration, err = er.acquireResource(state)
-
-		// Atomically, update the shared resource's new value & expiration.
-		er.cond.L.Lock()
-		if err == nil {
-			// Update resource & expiration, return the new value
-			resource = newValue
-			er.resource, er.expiration = resource, expiration
-		} else if !expired {
-			// An eager update failed. Discard the error and return the current--still valid--resource value
-			err = nil
-		}
-		er.acquiring = false // Indicate that no thread/goroutine is currently acquiring the resource
-
-		// Wake up any waiting threads/goroutines since there is a resource they can ALL use
-		er.cond.L.Unlock()
-		er.cond.Broadcast()
-	}
-	return resource, err // Return the resource this thread/goroutine can use
-}
diff --git a/sdk/azcore/runtime/policy_bearer_token.go b/sdk/azcore/runtime/policy_bearer_token.go
index cc9c8244ed..71e3062be0 100644
--- a/sdk/azcore/runtime/policy_bearer_token.go
+++ b/sdk/azcore/runtime/policy_bearer_token.go
@@ -10,12 +10,13 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/internal/temporal"
 )
 
 // BearerTokenPolicy authorizes requests with bearer tokens acquired from a TokenCredential.
 type BearerTokenPolicy struct {
 	// mainResource is the resource to be retreived using the tenant specified in the credential
-	mainResource *shared.ExpiringResource[azcore.AccessToken, acquiringResourceState]
+	mainResource *temporal.Resource[azcore.AccessToken, acquiringResourceState]
 	// the following fields are read-only
 	cred   azcore.TokenCredential
 	scopes []string
@@ -44,7 +45,7 @@ func NewBearerTokenPolicy(cred azcore.TokenCredential, scopes []string, opts *po
 	return &BearerTokenPolicy{
 		cred:         cred,
 		scopes:       scopes,
-		mainResource: shared.NewExpiringResource(acquire),
+		mainResource: temporal.NewResource(acquire),
 	}
 }
 
@@ -54,7 +55,7 @@ func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
 		p:   b,
 		req: req,
 	}
-	tk, err := b.mainResource.GetResource(as)
+	tk, err := b.mainResource.Get(as)
 	if err != nil {
 		return nil, err
 	}
diff --git a/sdk/azcore/runtime/policy_logging.go b/sdk/azcore/runtime/policy_logging.go
index 2faad35bb5..faf175e3fd 100644
--- a/sdk/azcore/runtime/policy_logging.go
+++ b/sdk/azcore/runtime/policy_logging.go
@@ -15,10 +15,10 @@ import (
 	"strings"
 	"time"
 
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/internal/diag"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 type logPolicy struct {
diff --git a/sdk/azcore/runtime/policy_retry.go b/sdk/azcore/runtime/policy_retry.go
index 187a09f534..9d630e4712 100644
--- a/sdk/azcore/runtime/policy_retry.go
+++ b/sdk/azcore/runtime/policy_retry.go
@@ -15,10 +15,10 @@ import (
 	"net/http"
 	"time"
 
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 const (
diff --git a/sdk/azcore/runtime/poller.go b/sdk/azcore/runtime/poller.go
index 394c9fff22..4624684249 100644
--- a/sdk/azcore/runtime/poller.go
+++ b/sdk/azcore/runtime/poller.go
@@ -15,13 +15,13 @@ import (
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
 )
 
 // FinalStateVia is the enumerated type for the possible final-state-via values.
diff --git a/sdk/data/azcosmos/CHANGELOG.md b/sdk/data/azcosmos/CHANGELOG.md
index 3af5d5d7ad..f3ad8a6de3 100644
--- a/sdk/data/azcosmos/CHANGELOG.md
+++ b/sdk/data/azcosmos/CHANGELOG.md
@@ -4,6 +4,8 @@
 
 ### Features Added
 
+* Added Transactional Batch support
+
 ### Breaking Changes
 
 ### Bugs Fixed
diff --git a/sdk/data/azcosmos/README.md b/sdk/data/azcosmos/README.md
index 50792db7b4..1663009c05 100644
--- a/sdk/data/azcosmos/README.md
+++ b/sdk/data/azcosmos/README.md
@@ -8,7 +8,7 @@ This client library enables client applications to connect to Azure Cosmos via t
 
 ### Prerequisites
 
-* Go versions 1.16 or higher
+* Go versions 1.18 or higher
 * An Azure subscription or free Azure Cosmos DB trial account
 
 Note: If you don't have an Azure subscription, create a free account before you begin.
diff --git a/sdk/data/azcosmos/cosmos_client.go b/sdk/data/azcosmos/cosmos_client.go
index b1d62a73cb..200097565b 100644
--- a/sdk/data/azcosmos/cosmos_client.go
+++ b/sdk/data/azcosmos/cosmos_client.go
@@ -238,6 +238,26 @@ func (c *Client) sendDeleteRequest(
 	return c.executeAndEnsureSuccessResponse(req)
 }
 
+func (c *Client) sendBatchRequest(
+	ctx context.Context,
+	path string,
+	batch []batchOperation,
+	operationContext pipelineRequestOptions,
+	requestOptions cosmosRequestOptions,
+	requestEnricher func(*policy.Request)) (*http.Response, error) {
+	req, err := c.createRequest(path, ctx, http.MethodPost, operationContext, requestOptions, requestEnricher)
+	if err != nil {
+		return nil, err
+	}
+
+	err = c.attachContent(batch, req)
+	if err != nil {
+		return nil, err
+	}
+
+	return c.executeAndEnsureSuccessResponse(req)
+}
+
 func (c *Client) createRequest(
 	path string,
 	ctx context.Context,
@@ -288,7 +308,6 @@ func (c *Client) attachContent(content interface{}, req *policy.Request) error {
 	default:
 		// Otherwise, we need to marshal it
 		err = azruntime.MarshalAsJSON(req, content)
-
 	}
 
 	if err != nil {
diff --git a/sdk/data/azcosmos/cosmos_container.go b/sdk/data/azcosmos/cosmos_container.go
index ad70384311..e9a594cbb0 100644
--- a/sdk/data/azcosmos/cosmos_container.go
+++ b/sdk/data/azcosmos/cosmos_container.go
@@ -5,6 +5,7 @@ package azcosmos
 
 import (
 	"context"
+	"errors"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
@@ -456,6 +457,66 @@ func (c *ContainerClient) NewQueryItemsPager(query string, partitionKey Partitio
 	})
 }
 
+// NewTransactionalBatch creates a batch of operations to be committed as a single unit.
+// See https://docs.microsoft.com/azure/cosmos-db/sql/transactional-batch
+func (c *ContainerClient) NewTransactionalBatch(partitionKey PartitionKey) TransactionalBatch {
+	return TransactionalBatch{partitionKey: partitionKey}
+}
+
+// ExecuteTransactionalBatch executes a transactional batch.
+// Once executed, verify the Success property of the response to determine if the batch was committed
+func (c *ContainerClient) ExecuteTransactionalBatch(ctx context.Context, b TransactionalBatch, o *TransactionalBatchOptions) (TransactionalBatchResponse, error) {
+	if len(b.operations) == 0 {
+		return TransactionalBatchResponse{}, errors.New("no operations in batch")
+	}
+
+	h := headerOptionsOverride{
+		partitionKey: &b.partitionKey,
+	}
+
+	if o == nil {
+		o = &TransactionalBatchOptions{}
+	} else {
+		h.enableContentResponseOnWrite = &o.EnableContentResponseOnWrite
+	}
+
+	// If contentResponseOnWrite is not enabled at the client level the
+	// service will not even send a batch response payload
+	// Instead we should automatically enforce contentResponseOnWrite for all
+	// batch requests whenever at least one of the item operations requires a content response (read operation)
+	enableContentResponseOnWriteForReadOperations := true
+	for _, op := range b.operations {
+		if op.getOperationType() == operationTypeRead {
+			h.enableContentResponseOnWrite = &enableContentResponseOnWriteForReadOperations
+			break
+		}
+	}
+
+	operationContext := pipelineRequestOptions{
+		resourceType:          resourceTypeDocument,
+		resourceAddress:       c.link,
+		isWriteOperation:      true,
+		headerOptionsOverride: &h}
+
+	path, err := generatePathForNameBased(resourceTypeDocument, operationContext.resourceAddress, true)
+	if err != nil {
+		return TransactionalBatchResponse{}, err
+	}
+
+	azResponse, err := c.database.client.sendBatchRequest(
+		ctx,
+		path,
+		b.operations,
+		operationContext,
+		o,
+		nil)
+	if err != nil {
+		return TransactionalBatchResponse{}, err
+	}
+
+	return newTransactionalBatchResponse(azResponse)
+}
+
 func (c *ContainerClient) getRID(ctx context.Context) (string, error) {
 	containerResponse, err := c.Read(ctx, nil)
 	if err != nil {
diff --git a/sdk/data/azcosmos/cosmos_headers.go b/sdk/data/azcosmos/cosmos_headers.go
index c0c7faebc4..d054bd652b 100644
--- a/sdk/data/azcosmos/cosmos_headers.go
+++ b/sdk/data/azcosmos/cosmos_headers.go
@@ -30,6 +30,9 @@ const (
 	cosmosHeaderQueryMetrics                       string = "x-ms-documentdb-query-metrics"
 	cosmosHeaderIndexUtilization                   string = "x-ms-cosmos-index-utilization"
 	cosmosHeaderCorrelatedActivityId               string = "x-ms-cosmos-correlated-activityid"
+	cosmosHeaderIsBatchRequest                     string = "x-ms-cosmos-is-batch-request"
+	cosmosHeaderIsBatchAtomic                      string = "x-ms-cosmos-batch-atomic"
+	cosmosHeaderIsBatchOrdered                     string = "x-ms-cosmos-batch-ordered"
 	headerXmsDate                                  string = "x-ms-date"
 	headerAuthorization                            string = "Authorization"
 	headerContentType                              string = "Content-Type"
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch.go b/sdk/data/azcosmos/cosmos_transactional_batch.go
new file mode 100644
index 0000000000..167fb55890
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch.go
@@ -0,0 +1,201 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+)
+
+// TransactionalBatch is a batch of operations to be executed in a single transaction.
+// See https://docs.microsoft.com/azure/cosmos-db/sql/transactional-batch
+type TransactionalBatch struct {
+	partitionKey PartitionKey
+	operations   []batchOperation
+}
+
+// CreateItem adds a create operation to the batch.
+func (b *TransactionalBatch) CreateItem(item []byte, o *TransactionalBatchItemOptions) {
+	b.operations = append(b.operations,
+		batchOperationCreate{
+			operationType: "Create",
+			resourceBody:  item})
+}
+
+// DeleteItem adds a delete operation to the batch.
+func (b *TransactionalBatch) DeleteItem(itemID string, o *TransactionalBatchItemOptions) {
+	if o == nil {
+		o = &TransactionalBatchItemOptions{}
+	}
+	b.operations = append(b.operations,
+		batchOperationDelete{
+			operationType: "Delete",
+			id:            itemID,
+			ifMatch:       o.IfMatchETag})
+}
+
+// ReplaceItem adds a replace operation to the batch.
+func (b *TransactionalBatch) ReplaceItem(itemID string, item []byte, o *TransactionalBatchItemOptions) {
+	if o == nil {
+		o = &TransactionalBatchItemOptions{}
+	}
+	b.operations = append(b.operations,
+		batchOperationReplace{
+			operationType: "Replace",
+			id:            itemID,
+			resourceBody:  item,
+			ifMatch:       o.IfMatchETag})
+}
+
+// UpsertItem adds an upsert operation to the batch.
+func (b *TransactionalBatch) UpsertItem(item []byte, o *TransactionalBatchItemOptions) {
+	if o == nil {
+		o = &TransactionalBatchItemOptions{}
+	}
+	b.operations = append(b.operations,
+		batchOperationUpsert{
+			operationType: "Upsert",
+			resourceBody:  item,
+			ifMatch:       o.IfMatchETag})
+}
+
+// ReadItem adds a read operation to the batch.
+func (b *TransactionalBatch) ReadItem(itemID string, o *TransactionalBatchItemOptions) {
+	b.operations = append(b.operations,
+		batchOperationRead{
+			operationType: "Read",
+			id:            itemID})
+}
+
+type batchOperation interface {
+	getOperationType() operationType
+}
+
+type batchOperationCreate struct {
+	operationType string
+	resourceBody  []byte
+}
+
+func (b batchOperationCreate) getOperationType() operationType {
+	return operationTypeCreate
+}
+
+// MarshalJSON implements the json.Marshaler interface
+func (b batchOperationCreate) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	buffer.WriteString(fmt.Sprintf("\"operationType\":\"%s\"", b.operationType))
+	buffer.WriteString(",\"resourceBody\":")
+	buffer.Write(b.resourceBody)
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+type batchOperationDelete struct {
+	operationType string
+	ifMatch       *azcore.ETag
+	id            string
+}
+
+func (b batchOperationDelete) getOperationType() operationType {
+	return operationTypeDelete
+}
+
+// MarshalJSON implements the json.Marshaler interface
+func (b batchOperationDelete) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	buffer.WriteString(fmt.Sprintf("\"operationType\":\"%s\"", b.operationType))
+	buffer.WriteString(fmt.Sprintf(",\"id\":\"%s\"", b.id))
+	if b.ifMatch != nil {
+		buffer.WriteString(",\"ifMatch\":")
+		etag, err := json.Marshal(b.ifMatch)
+		if err != nil {
+			return nil, err
+		}
+		buffer.Write(etag)
+	}
+
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+type batchOperationReplace struct {
+	operationType string
+	ifMatch       *azcore.ETag
+	id            string
+	resourceBody  []byte
+}
+
+func (b batchOperationReplace) getOperationType() operationType {
+	return operationTypeReplace
+}
+
+// MarshalJSON implements the json.Marshaler interface
+func (b batchOperationReplace) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	buffer.WriteString(fmt.Sprintf("\"operationType\":\"%s\"", b.operationType))
+	if b.ifMatch != nil {
+		buffer.WriteString(",\"ifMatch\":")
+		etag, err := json.Marshal(b.ifMatch)
+		if err != nil {
+			return nil, err
+		}
+		buffer.Write(etag)
+	}
+
+	buffer.WriteString(fmt.Sprintf(",\"id\":\"%s\"", b.id))
+	buffer.WriteString(",\"resourceBody\":")
+	buffer.Write(b.resourceBody)
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+type batchOperationUpsert struct {
+	operationType string
+	ifMatch       *azcore.ETag
+	resourceBody  []byte
+}
+
+func (b batchOperationUpsert) getOperationType() operationType {
+	return operationTypeUpsert
+}
+
+// MarshalJSON implements the json.Marshaler interface
+func (b batchOperationUpsert) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	buffer.WriteString(fmt.Sprintf("\"operationType\":\"%s\"", b.operationType))
+	if b.ifMatch != nil {
+		buffer.WriteString(",\"ifMatch\":")
+		etag, err := json.Marshal(b.ifMatch)
+		if err != nil {
+			return nil, err
+		}
+		buffer.Write(etag)
+	}
+
+	buffer.WriteString(",\"resourceBody\":")
+	buffer.Write(b.resourceBody)
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+type batchOperationRead struct {
+	operationType string
+	id            string
+}
+
+func (b batchOperationRead) getOperationType() operationType {
+	return operationTypeRead
+}
+
+// MarshalJSON implements the json.Marshaler interface
+func (b batchOperationRead) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	buffer.WriteString(fmt.Sprintf("\"operationType\":\"%s\"", b.operationType))
+	buffer.WriteString(fmt.Sprintf(",\"id\":\"%s\"", b.id))
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch_options.go b/sdk/data/azcosmos/cosmos_transactional_batch_options.go
new file mode 100644
index 0000000000..22ed6ddd57
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch_options.go
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+)
+
+// TransactionalBatchOptions includes options for transactional batch operations.
+type TransactionalBatchOptions struct {
+	// SessionToken to be used when using Session consistency on the account.
+	// When working with Session consistency, each new write request to Azure Cosmos DB is assigned a new SessionToken.
+	// The client instance will use this token internally with each read/query request to ensure that the set consistency level is maintained.
+	// In some scenarios you need to manage this Session yourself: Consider a web application with multiple nodes, each node will have its own client instance.
+	// If you wanted these nodes to participate in the same session (to be able read your own writes consistently across web tiers),
+	// you would have to send the SessionToken from the response of the write action on one node to the client tier, using a cookie or some other mechanism, and have that token flow back to the web tier for subsequent reads.
+	// If you are using a round-robin load balancer which does not maintain session affinity between requests, such as the Azure Load Balancer,the read could potentially land on a different node to the write request, where the session was created.
+	SessionToken string
+	// ConsistencyLevel overrides the account defined consistency level for this operation.
+	// Consistency can only be relaxed.
+	ConsistencyLevel *ConsistencyLevel
+	// When EnableContentResponseOnWrite is false, the operations in the batch response will have no body, except when they are Read operations.
+	// The default is false.
+	EnableContentResponseOnWrite bool
+}
+
+// TransactionalBatchItemOptions includes options for the specific operation inside a TransactionalBatch
+type TransactionalBatchItemOptions struct {
+	// IfMatchETag is used to ensure optimistic concurrency control.
+	// https://docs.microsoft.com/azure/cosmos-db/sql/database-transactions-optimistic-concurrency#optimistic-concurrency-control
+	IfMatchETag *azcore.ETag
+}
+
+func (options *TransactionalBatchOptions) toHeaders() *map[string]string {
+	headers := make(map[string]string, 2)
+
+	if options.ConsistencyLevel != nil {
+		headers[cosmosHeaderConsistencyLevel] = string(*options.ConsistencyLevel)
+	}
+
+	if options.SessionToken != "" {
+		headers[cosmosHeaderSessionToken] = options.SessionToken
+	}
+
+	headers[cosmosHeaderIsBatchRequest] = "True"
+	headers[cosmosHeaderIsBatchAtomic] = "True"
+	headers[cosmosHeaderIsBatchOrdered] = "True"
+
+	return &headers
+}
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch_response.go b/sdk/data/azcosmos/cosmos_transactional_batch_response.go
new file mode 100644
index 0000000000..09f8ee5790
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch_response.go
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
+)
+
+// TransactionalBatchResponse contains response from a transactional batch operation.
+type TransactionalBatchResponse struct {
+	Response
+	// SessionToken contains the value from the session token header to be used on session consistency.
+	SessionToken string
+	// OperationResults contains the individual batch operation results.
+	// The order of the results is the same as the order of the operations in the batch.
+	OperationResults []TransactionalBatchResult
+	// Success indicates if the transaction was successfully committed.
+	// If false, one of the operations in the batch failed.
+	// Inspect the OperationResults, any operation with status code http.StatusFailedDependency is a dependency failure.
+	// The cause of the batch failure is the first operation with status code different from http.StatusFailedDependency.
+	Success bool
+}
+
+func newTransactionalBatchResponse(resp *http.Response) (TransactionalBatchResponse, error) {
+	response := TransactionalBatchResponse{
+		Response: newResponse(resp),
+	}
+
+	response.SessionToken = resp.Header.Get(cosmosHeaderSessionToken)
+
+	response.Success = resp.StatusCode != http.StatusMultiStatus
+
+	if err := runtime.UnmarshalAsJSON(resp, &response.OperationResults); err != nil {
+		return TransactionalBatchResponse{}, err
+	}
+
+	return response, nil
+}
+
+// TransactionalBatchResult represents the result of a single operation in a batch.
+type TransactionalBatchResult struct {
+	// StatusCode contains the status code of the operation.
+	StatusCode int32
+	// RequestCharge contains the request charge for the operation.
+	RequestCharge float32
+	// ResourceBody contains the body response of the operation.
+	// This property is available depending on the EnableContentResponseOnWrite option.
+	ResourceBody []byte
+	// ETag contains the ETag of the operation.
+	ETag azcore.ETag
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface
+func (or *TransactionalBatchResult) UnmarshalJSON(b []byte) error {
+	var attributes map[string]json.RawMessage
+	err := json.Unmarshal(b, &attributes)
+	if err != nil {
+		return err
+	}
+
+	if statusCode, ok := attributes["statusCode"]; ok {
+		if err := json.Unmarshal(statusCode, &or.StatusCode); err != nil {
+			return err
+		}
+	}
+
+	if requestCharge, ok := attributes["requestCharge"]; ok {
+		if err := json.Unmarshal(requestCharge, &or.RequestCharge); err != nil {
+			return err
+		}
+	}
+
+	if etag, ok := attributes["eTag"]; ok {
+		if err := json.Unmarshal(etag, &or.ETag); err != nil {
+			return err
+		}
+	}
+
+	if body, ok := attributes["resourceBody"]; ok {
+		or.ResourceBody = body
+	}
+
+	return nil
+}
diff --git a/sdk/data/azcosmos/doc.go b/sdk/data/azcosmos/doc.go
index b8689fa183..622300a0d2 100644
--- a/sdk/data/azcosmos/doc.go
+++ b/sdk/data/azcosmos/doc.go
@@ -49,6 +49,7 @@ The following sections provide several code snippets covering some of the most c
 	- Creating a container
 	- Creating, reading, and deleting items
 	- Querying items
+	- Using Transactional Batch
 
 
 Creating a database
@@ -135,5 +136,48 @@ Querying items
 			json.Unmarshal(item, &itemResponseBody)
 		}
 	}
+
+Using Transactional batch
+
+	pk := azcosmos.NewPartitionKeyString("myPartitionKeyValue")
+	batch := container.NewTransactionalBatch(pk)
+
+	item := map[string]string{
+		"id":    "1",
+		"myPartitionKeyProperty": "myPartitionKeyValue",
+		"otherValue": 10
+	}
+	marshalled, err := json.Marshal(item)
+	handle(err)
+
+	batch.CreateItem(marshalled, nil)
+	batch.ReadItem("otherExistingId", nil)
+	batch.DeleteItem("yetAnotherExistingId", nil)
+
+	batchResponse, err  := container.ExecuteTransactionalBatch(context, batch, nil)
+	handle(err)
+
+	if batchResponse.Success {
+		// Transaction succeeded
+		// We can inspect the individual operation results
+		for index, operation := range batchResponse.OperationResults {
+			fmt.Printf("Operation %v completed with status code %v consumed %v RU", index, operation.StatusCode, operation.RequestCharge)
+			if index == 1 {
+				// Read operation would have body available
+				var itemResponseBody map[string]string
+				err = json.Unmarshal(operation.ResourceBody, &itemResponseBody)
+				if err != nil {
+					panic(err)
+				}
+			}
+		}
+	} else {
+		// Transaction failed, look for the offending operation
+		for index, operation := range batchResponse.OperationResults {
+			if operation.StatusCode != http.StatusFailedDependency {
+				fmt.Printf("Transaction failed due to operation %v which failed with status code %v", index, operation.StatusCode)
+			}
+		}
+	}
 */
 package azcosmos
diff --git a/sdk/data/azcosmos/operation_type.go b/sdk/data/azcosmos/operation_type.go
new file mode 100644
index 0000000000..a37b64df99
--- /dev/null
+++ b/sdk/data/azcosmos/operation_type.go
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+// operationType defines supported values for operations.
+type operationType int
+
+const (
+	operationTypeCreate  operationType = 0
+	operationTypeRead    operationType = 2
+	operationTypeReplace operationType = 5
+	operationTypeDelete  operationType = 4
+	operationTypeUpsert  operationType = 20
+)
diff --git a/sdk/internal/CHANGELOG.md b/sdk/internal/CHANGELOG.md
index 525b3dcb7e..728ea6c736 100644
--- a/sdk/internal/CHANGELOG.md
+++ b/sdk/internal/CHANGELOG.md
@@ -1,6 +1,16 @@
 # Release History
 
-## 1.0.0 (2022-05-10)
+## 1.0.1 (Unreleased)
+
+### Features Added
+
+### Breaking Changes
+
+### Bugs Fixed
+
+### Other Changes
+
+## 1.0.0 (2022-05-12)
 
 ### Features Added
 * Added `temporal` package for handling of temporal resources.
diff --git a/sdk/internal/version.go b/sdk/internal/version.go
index 95f096c7b4..4ee0a2a8f6 100644
--- a/sdk/internal/version.go
+++ b/sdk/internal/version.go
@@ -11,5 +11,5 @@ const (
 	Module = "internal"
 
 	// Version is the semantic version (see http://semver.org) of this module.
-	Version = "v1.0.0"
+	Version = "v1.0.1"
 )
diff --git a/sdk/messaging/azservicebus/CHANGELOG.md b/sdk/messaging/azservicebus/CHANGELOG.md
index 9019bf3acd..89af6aafe9 100644
--- a/sdk/messaging/azservicebus/CHANGELOG.md
+++ b/sdk/messaging/azservicebus/CHANGELOG.md
@@ -21,11 +21,14 @@
 
 - `admin.Client` can now be configured using `azcore.Options`. (#17796)
 - `ReceivedMessage.TransactionPartitionKey` has been removed as this library doesn't support transactions.
+- `ReceivedMessage.Body()` is now a field. `Body` will be nil in the cases where it would have returned an error (where the underlying AMQP message had a payload in .Value, .Sequence or had multiple byte slices in .Data). (#17888)
 
 ### Bugs Fixed
 
 - Fixing issue where the AcceptNextSessionForQueue and AcceptNextSessionForSubscription 
   couldn't be cancelled, forcing the user to wait for the service to timeout. (#17598)
+- Fixing bug where there was a chance that internally cached messages would not be returned when
+  the receiver was draining. (#17893)
 
 ### Other Changes
 
diff --git a/sdk/messaging/azservicebus/README.md b/sdk/messaging/azservicebus/README.md
index 3eca9c09b8..bff21758a0 100644
--- a/sdk/messaging/azservicebus/README.md
+++ b/sdk/messaging/azservicebus/README.md
@@ -219,13 +219,30 @@ if err != nil {
 }
 
 for _, message := range messages {
+  // The message body is a []byte. For this example we're just assuming that the body
+  // was a string, converted to bytes but any []byte payload is valid.
+  var body []byte = message.Body
+  fmt.Printf("Message received with body: %s\n", string(body))
+
   // For more information about settling messages:
   // https://docs.microsoft.com/azure/service-bus-messaging/message-transfers-locks-settlement#settling-receive-operations
   err = receiver.CompleteMessage(context.TODO(), message, nil)
 
   if err != nil {
+    var sbErr *azservicebus.Error
+
+    if errors.As(err, &sbErr) && sbErr.Code == azservicebus.CodeLockLost {
+      // The message lock has expired. This isn't fatal for the client, but it does mean
+      // that this message can be received by another Receiver (or potentially this one!).
+      fmt.Printf("Message lock expired\n")
+
+      // You can extend the message lock by calling receiver.RenewMessageLock(msg) before the
+      // message lock has expired.
+      continue
+    }
+
     panic(err)
-  }
+	}
 
   fmt.Printf("Received and completed the message\n")
 }
diff --git a/sdk/messaging/azservicebus/internal/amqpLinks.go b/sdk/messaging/azservicebus/internal/amqpLinks.go
index 7b796b58af..240fe45c77 100644
--- a/sdk/messaging/azservicebus/internal/amqpLinks.go
+++ b/sdk/messaging/azservicebus/internal/amqpLinks.go
@@ -328,7 +328,7 @@ func (l *AMQPLinksImpl) Retry(ctx context.Context, eventName log.Event, operatio
 				// Whereas normally you'd do (for non-detach errors):
 				//   0th attempt
 				//   (actual retries)
-				log.Writef(exported.EventConn, "Link was previously detached. Attempting quick reconnect to recover from error: %s", err.Error())
+				log.Writef(exported.EventConn, "(%s) Link was previously detached. Attempting quick reconnect to recover from error: %s", operation, err.Error())
 				didQuickRetry = true
 				args.ResetAttempts()
 			}
diff --git a/sdk/messaging/azservicebus/internal/cbs.go b/sdk/messaging/azservicebus/internal/cbs.go
index 5a5419b004..33a31e86ed 100644
--- a/sdk/messaging/azservicebus/internal/cbs.go
+++ b/sdk/messaging/azservicebus/internal/cbs.go
@@ -25,8 +25,9 @@ const (
 // NegotiateClaim attempts to put a token to the $cbs management endpoint to negotiate auth for the given audience
 func NegotiateClaim(ctx context.Context, audience string, conn *amqp.Client, provider auth.TokenProvider) error {
 	link, err := NewRPCLink(RPCLinkArgs{
-		Client:  &amqpwrap.AMQPClientWrapper{Inner: conn},
-		Address: cbsAddress,
+		Client:   &amqpwrap.AMQPClientWrapper{Inner: conn},
+		Address:  cbsAddress,
+		LogEvent: exported.EventAuth,
 	})
 
 	if err != nil {
diff --git a/sdk/messaging/azservicebus/internal/namespace.go b/sdk/messaging/azservicebus/internal/namespace.go
index 45e86a23cb..ba5a144c3b 100644
--- a/sdk/messaging/azservicebus/internal/namespace.go
+++ b/sdk/messaging/azservicebus/internal/namespace.go
@@ -214,8 +214,9 @@ func (ns *Namespace) NewRPCLink(ctx context.Context, managementPath string) (RPC
 	}
 
 	return NewRPCLink(RPCLinkArgs{
-		Client:  &amqpwrap.AMQPClientWrapper{Inner: client},
-		Address: managementPath,
+		Client:   &amqpwrap.AMQPClientWrapper{Inner: client},
+		Address:  managementPath,
+		LogEvent: exported.EventReceiver,
 	})
 }
 
diff --git a/sdk/messaging/azservicebus/internal/rpc.go b/sdk/messaging/azservicebus/internal/rpc.go
index 0b24610782..1f34b1a2ab 100644
--- a/sdk/messaging/azservicebus/internal/rpc.go
+++ b/sdk/messaging/azservicebus/internal/rpc.go
@@ -38,6 +38,8 @@ type (
 		responseMu              sync.Mutex
 		startResponseRouterOnce *sync.Once
 		responseMap             map[string]chan rpcResponse
+		rpcLinkCtx              context.Context
+		rpcLinkCtxCancel        context.CancelFunc
 		broadcastErr            error // the error that caused the responseMap to be nil'd
 
 		logEvent azlog.Event
@@ -156,6 +158,7 @@ func NewRPCLink(args RPCLinkArgs) (*rpcLink, error) {
 
 	link.sender = sender
 	link.receiver = receiver
+	link.rpcLinkCtx, link.rpcLinkCtxCancel = context.WithCancel(context.Background())
 
 	return link, nil
 }
@@ -169,13 +172,15 @@ func (l *rpcLink) startResponseRouter() {
 	defer azlog.Writef(l.logEvent, responseRouterShutdownMessage)
 
 	for {
-		res, err := l.receiver.Receive(context.Background())
+		res, err := l.receiver.Receive(l.rpcLinkCtx)
 
 		if err != nil {
 			// if the link or connection has a malfunction that would require it to restart then
 			// we need to bail out, broadcasting to all affected callers/consumers.
 			if GetRecoveryKind(err) != RecoveryKindNone {
-				azlog.Writef(l.logEvent, "Error in RPCLink, stopping response router: %s", err.Error())
+				if !IsCancelError(err) {
+					azlog.Writef(l.logEvent, "Error in RPCLink, stopping response router: %s", err.Error())
+				}
 				l.broadcastError(err)
 				break
 			}
@@ -317,6 +322,8 @@ func (l *rpcLink) RPC(ctx context.Context, msg *amqp.Message) (*RPCResponse, err
 
 // Close the link receiver, sender and session
 func (l *rpcLink) Close(ctx context.Context) error {
+	l.rpcLinkCtxCancel()
+
 	if err := l.closeReceiver(ctx); err != nil {
 		_ = l.closeSender(ctx)
 		_ = l.closeSession(ctx)
diff --git a/sdk/messaging/azservicebus/internal/stress/shared/stress_context.go b/sdk/messaging/azservicebus/internal/stress/shared/stress_context.go
index d3a7a48d48..45cacf0570 100644
--- a/sdk/messaging/azservicebus/internal/stress/shared/stress_context.go
+++ b/sdk/messaging/azservicebus/internal/stress/shared/stress_context.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	azlog "github.com/Azure/azure-sdk-for-go/sdk/azcore/log"
+	"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus"
 	"github.com/microsoft/ApplicationInsights-Go/appinsights"
 )
 
@@ -64,7 +65,7 @@ func MustCreateStressContext(testName string) *StressContext {
 
 	ctx, cancel := NewCtrlCContext()
 
-	azlog.SetEvents("azsb.Conn", "azsb.Auth", "azsb.Retry", "azsb.Mgmt")
+	azlog.SetEvents(azservicebus.EventAdmin, azservicebus.EventAuth, azservicebus.EventConn, azservicebus.EventReceiver, azservicebus.EventSender)
 
 	logMessages := make(chan string, 10000)
 
diff --git a/sdk/messaging/azservicebus/message.go b/sdk/messaging/azservicebus/message.go
index 43a4106e6f..1111d711e8 100644
--- a/sdk/messaging/azservicebus/message.go
+++ b/sdk/messaging/azservicebus/message.go
@@ -4,7 +4,6 @@
 package azservicebus
 
 import (
-	"errors"
 	"fmt"
 	"time"
 
@@ -18,6 +17,9 @@ type ReceivedMessage struct {
 	// ApplicationProperties can be used to store custom metadata for a message.
 	ApplicationProperties map[string]interface{}
 
+	// Body is the payload for a message.
+	Body []byte
+
 	// ContentType describes the payload of the message, with a descriptor following
 	// the format of Content-Type, specified by RFC2045 (ex: "application/json").
 	ContentType *string
@@ -138,17 +140,6 @@ const (
 	MessageStateScheduled MessageState = 2
 )
 
-// Body returns the body for this received message.
-// If the body not compatible with ReceivedMessage this function will return an error.
-func (rm *ReceivedMessage) Body() ([]byte, error) {
-	// TODO: does this come back as a zero length array if the body is empty (which is allowed)
-	if rm.rawAMQPMessage.Data == nil || len(rm.rawAMQPMessage.Data) != 1 {
-		return nil, errors.New("AMQP message Data section is improperly encoded for ReceivedMessage")
-	}
-
-	return rm.rawAMQPMessage.Data[0], nil
-}
-
 // Message is a message with a body and commonly used properties.
 // Properties that are pointers are optional.
 type Message struct {
@@ -323,6 +314,10 @@ func newReceivedMessage(amqpMsg *amqp.Message) *ReceivedMessage {
 		State:          MessageStateActive,
 	}
 
+	if len(msg.rawAMQPMessage.Data) == 1 {
+		msg.Body = msg.rawAMQPMessage.Data[0]
+	}
+
 	if amqpMsg.Properties != nil {
 		if id, ok := amqpMsg.Properties.MessageID.(string); ok {
 			msg.MessageID = id
diff --git a/sdk/messaging/azservicebus/receiver.go b/sdk/messaging/azservicebus/receiver.go
index 588e753585..45d9442213 100644
--- a/sdk/messaging/azservicebus/receiver.go
+++ b/sdk/messaging/azservicebus/receiver.go
@@ -373,19 +373,22 @@ func (r *Receiver) receiveMessagesImpl(ctx context.Context, maxMessages int, opt
 	}
 
 	flushAndReturn := func(lastErr error) ([]*ReceivedMessage, error) {
-		flushPrefetchedMessages(ctx, linksWithID.Receiver, &all)
+		flushPrefetchedMessages(linksWithID.Receiver, &all)
 
 		if len(all) > 0 {
 			// users don't typically check the other values in the return if the 'err' is non-nil
 			// so if we have any messages we'll just return them and nil out the error.
 			// If it's persistent they'll see it on their next ReceiveMessages() call.
+			log.Writef(EventReceiver, "Received %d/%d messages", len(all), maxMessages)
 			return all, nil
 		} else {
 			if internal.GetRecoveryKind(lastErr) == internal.RecoveryKindFatal {
+				log.Writef(EventReceiver, "Fatal error when receiving: %#v", lastErr)
 				return nil, lastErr
 			}
 
 			// there's no material difference here, so let them fail on the next call to ReceiveMessages() instead.
+			log.Writef(EventReceiver, "Non-fatal error occurred and no messages were received. Will recover on next call: %v", lastErr)
 			return nil, nil
 		}
 	}
@@ -399,10 +402,14 @@ func (r *Receiver) receiveMessagesImpl(ctx context.Context, maxMessages int, opt
 			return flushAndReturn(err)
 		}
 
-		// user cancelled, fall through so we drain the link since it's still active.
+		// user cancelled (or we hit our "afte first message" timer).
+		// Fall through so we drain the link since it's still active.
+		log.Writef(EventReceiver, "Context has been cancelled")
 	}
 
 	if len(all) < maxMessages { // drain if there are excess credits
+		log.Writef(EventReceiver, "Draining to clear %d credits", maxMessages-len(all))
+
 		drainCtx, drainCtxcancel := context.WithTimeout(context.Background(), r.defaultDrainTimeout)
 		defer drainCtxcancel()
 
@@ -414,6 +421,8 @@ func (r *Receiver) receiveMessagesImpl(ctx context.Context, maxMessages int, opt
 			cleanup(err)
 
 			if internal.IsCancelError(err) {
+				log.Writef(EventReceiver, "Drain didn't complete in %s, link was recreated", r.defaultDrainTimeout)
+
 				// this cancellation is entirely on us - we cleaned up as best we can
 				// but the user isn't expected to know about this detail so we don't
 				// return the cancellation error.
@@ -428,10 +437,13 @@ func (r *Receiver) receiveMessagesImpl(ctx context.Context, maxMessages int, opt
 
 	// we only get here if we got exactly the # of messages they asked for
 	// in the initial set of link.Receive() calls above.
+	log.Writef(EventReceiver, "Received %d/%d messages", len(all), maxMessages)
 	return all, nil
 }
 
 func fetchMessages(ctx context.Context, receiver internal.AMQPReceiver, maxMessages int, defaultTimeAfterFirstMessage time.Duration, messages *[]*ReceivedMessage) error {
+	log.Writef(EventReceiver, "Fetching messages, issuing %d credits", maxMessages)
+
 	if err := receiver.IssueCredit(uint32(maxMessages)); err != nil {
 		return err
 	}
@@ -462,9 +474,16 @@ func fetchMessages(ctx context.Context, receiver internal.AMQPReceiver, maxMessa
 
 // flushPrefetchedMessages makes a best-effort attempt at flushing the internal channel for the link. This is
 // a purely in-memory operation so should work even if the link itself has failed.
-func flushPrefetchedMessages(ctx context.Context, receiver internal.AMQPReceiver, messages *[]*ReceivedMessage) {
+func flushPrefetchedMessages(receiver internal.AMQPReceiver, messages *[]*ReceivedMessage) {
+	count := 0
+	defer func() {
+		if count > 0 {
+			log.Writef(EventReceiver, "Flushed %d messages from receiver's internal cache", count)
+		}
+	}()
+
 	for {
-		am, err := receiver.Prefetched(ctx)
+		am, err := receiver.Prefetched(context.Background())
 
 		// we've removed any code of consequence from Prefetched.
 		if am == nil || err != nil {
@@ -472,6 +491,7 @@ func flushPrefetchedMessages(ctx context.Context, receiver internal.AMQPReceiver
 		}
 
 		*messages = append(*messages, newReceivedMessage(am))
+		count++
 	}
 }
 


Test patch:
diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1
index 4c83ad3164..b7785df9dd 100644
--- a/eng/common/TestResources/New-TestResources.ps1
+++ b/eng/common/TestResources/New-TestResources.ps1
@@ -375,7 +375,7 @@ try {
             $BaseName = 't' + (New-Guid).ToString('n').Substring(0, 16)
             Log "Generated base name '$BaseName' for CI build"
         } else {
-            $BaseName = GetBaseName $UserName $ServiceDirectory
+            $BaseName = GetBaseName $UserName (GetServiceLeafDirectoryName $ServiceDirectory)
             Log "BaseName was not set. Using default base name '$BaseName'"
         }
     }
@@ -520,7 +520,7 @@ try {
         $ResourceGroupName
     } elseif ($CI) {
         # Format the resource group name based on resource group naming recommendations and limitations.
-        "rg-{0}-$BaseName" -f ($serviceName -replace '[\\\/:]', '-').Substring(0, [Math]::Min($serviceName.Length, 90 - $BaseName.Length - 4)).Trim('-')
+        "rg-{0}-$BaseName" -f ($serviceName -replace '[\.\\\/:]', '-').ToLowerInvariant().Substring(0, [Math]::Min($serviceName.Length, 90 - $BaseName.Length - 4)).Trim('-')
     } else {
         "rg-$BaseName"
     }
diff --git a/eng/common/TestResources/README.md b/eng/common/TestResources/README.md
index 8463501c0f..957a1f9c35 100644
--- a/eng/common/TestResources/README.md
+++ b/eng/common/TestResources/README.md
@@ -77,6 +77,49 @@ setx KEYVAULT_SKU ${env:KEYVAULT_SKU}
 setx AZURE_KEYVAULT_URL ${env:AZURE_KEYVAULT_URL}
 ```
 
+### Pre- and Post- Scripts
+
+Sometimes creating test resources requires either some work to be done prior to or after the main test-resources.json script is executed.
+For these scenarios a `test-resources-pre.ps1` or `test-resources-post.ps1`, respectively, can be created in the same folder as the `test-resources.json` file.
+
+For example, it may be necessary to create artifacts prior to provisioning the actual resource, such as a certificate.
+Typically the created artifact will need to be passed to `test-resources.json` to be used in the ARM template or as output (or both).
+
+Below is an example of how `$templateFileParameters` can be used to pass data from the `pre-` script to `test-resources.json`.
+
+**Snippet from `test-resources-pre.ps1`**
+```powershell
+$cert = New-X509Certificate2 -SubjectName 'E=opensource@microsoft.com, CN=Azure SDK, OU=Azure SDK, O=Microsoft, L=Frisco, S=TX, C=US' -ValidDays 3652
+# Create new entries in $templateFileParameters
+$templateFileParameters['ConfidentialLedgerPrincipalPEM'] = Format-X509Certificate2 -Certificate $cert
+$templateFileParameters['ConfidentialLedgerPrincipalPEMPK'] = Format-X509Certificate2 -Type Pkcs8 -Certificate $cert
+```
+
+**Snippet from the corresponding `test-resources.json`.**
+
+Note that the values present in `$templateFileParameters` will map to parameters of the same name.
+```json
+{
+  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+  "contentVersion": "1.0.0.0",
+  "parameters": {
+    "_comment": "Other required parameters would go here... (this is not part of the actual test-resources.json)",
+    "ConfidentialLedgerPrincipalPEM": {
+      "type": "string",
+      "metadata": {
+        "description": "The certificate to configure as a certBasedSecurityPrincipal."
+      }
+    },
+    "ConfidentialLedgerPrincipalPEMPK": {
+      "type": "string",
+      "metadata": {
+        "description": "The certificate to configure as a certBasedSecurityPrincipal."
+      }
+    }
+  },
+}
+```
+
 ### Cleaning up Resources
 
 By default, resource groups are tagged with a `DeleteAfter` value and date according to the default or specified
diff --git a/eng/common/TestResources/SubConfig-Helpers.ps1 b/eng/common/TestResources/SubConfig-Helpers.ps1
index 04cc7d3729..cc93def6aa 100644
--- a/eng/common/TestResources/SubConfig-Helpers.ps1
+++ b/eng/common/TestResources/SubConfig-Helpers.ps1
@@ -1,4 +1,5 @@
 function BuildServiceDirectoryPrefix([string]$serviceName) {
+    $serviceName = $serviceName -replace '[\./\\]', '_'
     return $serviceName.ToUpperInvariant() + "_"
 }
 
@@ -17,8 +18,8 @@ function GetUserName() {
 
 function GetBaseName([string]$user, [string]$serviceDirectoryName) {
     # Handle service directories in nested directories, e.g. `data/aztables`
-    $serviceDirectorySafeName = $serviceDirectoryName -replace '[/\\]', ''
-    return "$user$serviceDirectorySafeName"
+    $serviceDirectorySafeName = $serviceDirectoryName -replace '[\./\\]', ''
+    return "$user$serviceDirectorySafeName".ToLowerInvariant()
 }
 
 function ShouldMarkValueAsSecret([string]$serviceName, [string]$key, [string]$value, [array]$allowedValues = @())
diff --git a/sdk/azcore/internal/pollers/body/body_test.go b/sdk/azcore/internal/pollers/body/body_test.go
index 8fdf839978..9ec16ec872 100644
--- a/sdk/azcore/internal/pollers/body/body_test.go
+++ b/sdk/azcore/internal/pollers/body/body_test.go
@@ -157,16 +157,16 @@ func TestPollFailed(t *testing.T) {
 	resp := initialResponse(http.MethodPatch, strings.NewReader(`{ "properties": { "provisioningState": "Started" } }`))
 	poller, err := New[widget](exported.NewPipeline(shared.TransportFunc(func(req *http.Request) (*http.Response, error) {
 		return &http.Response{
-			StatusCode: http.StatusBadRequest,
+			StatusCode: http.StatusOK,
 			Header:     http.Header{},
-			Body:       io.NopCloser(strings.NewReader(`{ "provisioningState": "failed" }`)),
+			Body:       io.NopCloser(strings.NewReader(`{ "properties": { "provisioningState": "failed" } }`)),
 		}, nil
 	})), resp)
 	require.NoError(t, err)
 	require.False(t, poller.Done())
 	resp, err = poller.Poll(context.Background())
 	require.NoError(t, err)
-	require.Equal(t, http.StatusBadRequest, resp.StatusCode)
+	require.Equal(t, http.StatusOK, resp.StatusCode)
 	require.True(t, poller.Done())
 	var result widget
 	err = poller.Result(context.Background(), &result)
diff --git a/sdk/azcore/internal/pollers/loc/loc_test.go b/sdk/azcore/internal/pollers/loc/loc_test.go
index 4a3c25db83..f5fdd09b2f 100644
--- a/sdk/azcore/internal/pollers/loc/loc_test.go
+++ b/sdk/azcore/internal/pollers/loc/loc_test.go
@@ -9,9 +9,12 @@ package loc
 import (
 	"context"
 	"fmt"
+	"io"
 	"net/http"
+	"strings"
 	"testing"
 
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
 	"github.com/stretchr/testify/require"
@@ -62,6 +65,7 @@ func TestNew(t *testing.T) {
 	resp.Header.Set(shared.HeaderLocation, fakeLocationURL)
 	poller, err = New[struct{}](exported.Pipeline{}, resp)
 	require.NoError(t, err)
+	require.NotNil(t, poller)
 
 	resp = initialResponse()
 	resp.Header.Set(shared.HeaderLocation, "this is a bad polling URL")
@@ -117,6 +121,43 @@ func TestUpdateFailed(t *testing.T) {
 	require.False(t, poller.Done())
 	resp, err = poller.Poll(context.Background())
 	require.NoError(t, err)
+	require.Equal(t, http.StatusBadRequest, resp.StatusCode)
 	err = poller.Result(context.Background(), nil)
 	require.Error(t, err)
 }
+
+func TestUpdateFailedWithProvisioningState(t *testing.T) {
+	resp := initialResponse()
+	resp.Header.Set(shared.HeaderLocation, fakeLocationURL)
+	poller, err := New[struct{}](exported.NewPipeline(shared.TransportFunc(func(req *http.Request) (*http.Response, error) {
+		if surl := req.URL.String(); surl == fakeLocationURL {
+			resp := &http.Response{
+				StatusCode: http.StatusAccepted,
+				Body:       http.NoBody,
+				Header:     http.Header{},
+			}
+			resp.Header.Set(shared.HeaderLocation, fakeLocationURL2)
+			return resp, nil
+		} else if surl == fakeLocationURL2 {
+			return &http.Response{
+				StatusCode: http.StatusOK,
+				Body:       io.NopCloser(strings.NewReader(`{ "properties": { "provisioningState": "failed" } }`)),
+			}, nil
+		} else {
+			return nil, fmt.Errorf("test bug, unhandled URL %s", surl)
+		}
+	})), resp)
+	require.NoError(t, err)
+	require.False(t, poller.Done())
+	resp, err = poller.Poll(context.Background())
+	require.NoError(t, err)
+	require.Equal(t, http.StatusAccepted, resp.StatusCode)
+	require.False(t, poller.Done())
+	resp, err = poller.Poll(context.Background())
+	require.NoError(t, err)
+	require.Equal(t, http.StatusOK, resp.StatusCode)
+	require.True(t, poller.Done())
+	err = poller.Result(context.Background(), nil)
+	var respErr *azcore.ResponseError
+	require.ErrorAs(t, err, &respErr)
+}
diff --git a/sdk/azcore/internal/shared/expiring_resource_test.go b/sdk/azcore/internal/shared/expiring_resource_test.go
deleted file mode 100644
index 075cb1baff..0000000000
--- a/sdk/azcore/internal/shared/expiring_resource_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-//go:build go1.18
-// +build go1.18
-
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package shared
-
-import (
-	"errors"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestNewExpiringResource(t *testing.T) {
-	er := NewExpiringResource(func(state string) (newResource string, newExpiration time.Time, err error) {
-		switch state {
-		case "initial":
-			return "updated", time.Now().Add(-time.Minute), nil
-		case "updated":
-			return "refreshed", time.Now().Add(1 * time.Hour), nil
-		default:
-			t.Fatalf("unexpected state %s", state)
-			return "", time.Time{}, errors.New("unexpected")
-		}
-	})
-	res, err := er.GetResource("initial")
-	require.NoError(t, err)
-	require.Equal(t, "updated", res)
-	res, err = er.GetResource(res)
-	require.NoError(t, err)
-	require.Equal(t, "refreshed", res)
-	res, err = er.GetResource(res)
-	require.NoError(t, err)
-	require.Equal(t, "refreshed", res)
-}
-
-func TestExpiringResourceError(t *testing.T) {
-	expectedState := "expected state"
-	expectedError := "expected error"
-	calls := 0
-	er := NewExpiringResource(func(state string) (newResource string, newExpiration time.Time, err error) {
-		calls += 1
-		if calls == 1 {
-			return expectedState, time.Now().Add(time.Minute), nil
-		} else {
-			return "un" + expectedState, time.Time{}, errors.New(expectedError)
-		}
-	})
-	res, err := er.GetResource(expectedState)
-	require.NoError(t, err)
-	require.Equal(t, expectedState, res)
-
-	// When an eager update fails, GetResource should return the prior value and no error.
-	er.lastAttempt = time.Now().Add(-time.Hour)
-	for i := 0; i < 3; i++ {
-		res, err = er.GetResource(res)
-		require.NoError(t, err)
-		require.Equal(t, expectedState, res)
-		// GetResource should wait before trying a second eager update i.e. it shouldn't make a third call in this loop
-		require.Equal(t, 2, calls)
-	}
-
-	// After the resource has expired, GetResource should return any error from updating
-	er.expiration = time.Now().Add(-time.Hour)
-	_, err = er.GetResource(res)
-	require.Error(t, err, expectedError)
-	require.Equal(t, 3, calls)
-}
diff --git a/sdk/azcore/log/log_test.go b/sdk/azcore/log/log_test.go
index 0e281cd760..769a44559e 100644
--- a/sdk/azcore/log/log_test.go
+++ b/sdk/azcore/log/log_test.go
@@ -17,7 +17,7 @@ import (
 func TestLoggingDefault(t *testing.T) {
 	// ensure logging with nil listener doesn't fail
 	SetListener(nil)
-	log.Write(log.EventRequest, "this should work just fine")
+	log.Write(EventRequest, "this should work just fine")
 
 	testlog := map[Event]string{}
 	SetListener(func(cls Event, msg string) {
diff --git a/sdk/azcore/runtime/policy_logging_test.go b/sdk/azcore/runtime/policy_logging_test.go
index 18c3a43c61..e65442699d 100644
--- a/sdk/azcore/runtime/policy_logging_test.go
+++ b/sdk/azcore/runtime/policy_logging_test.go
@@ -15,7 +15,7 @@ import (
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
-	"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
 	"github.com/Azure/azure-sdk-for-go/sdk/internal/mock"
 )
 
diff --git a/sdk/data/azcosmos/cosmos_client_test.go b/sdk/data/azcosmos/cosmos_client_test.go
index 6bda337e82..93567db308 100644
--- a/sdk/data/azcosmos/cosmos_client_test.go
+++ b/sdk/data/azcosmos/cosmos_client_test.go
@@ -354,6 +354,50 @@ func TestSendQuery(t *testing.T) {
 	}
 }
 
+func TestSendBatch(t *testing.T) {
+	srv, close := mock.NewTLSServer()
+	defer close()
+	srv.SetResponse(
+		mock.WithStatusCode(200))
+	verifier := pipelineVerifier{}
+	pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
+	client := &Client{endpoint: srv.URL(), pipeline: pl}
+	operationContext := pipelineRequestOptions{
+		resourceType:    resourceTypeDocument,
+		resourceAddress: "",
+	}
+
+	batch := TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+
+	body := map[string]string{
+		"foo": "bar",
+	}
+
+	itemMarshall, _ := json.Marshal(body)
+
+	batch.CreateItem(itemMarshall, nil)
+	batch.ReadItem("someId", nil)
+
+	marshalled, err := json.Marshal(batch.operations)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = client.sendBatchRequest(context.Background(), "/", batch.operations, operationContext, &TransactionalBatchOptions{}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if verifier.requests[0].method != http.MethodPost {
+		t.Errorf("Expected %v, but got %v", http.MethodPost, verifier.requests[0].method)
+	}
+
+	if verifier.requests[0].body != string(marshalled) {
+		t.Errorf("Expected %v, but got %v", string(marshalled), verifier.requests[0].body)
+	}
+}
+
 func TestCreateScopeFromEndpoint(t *testing.T) {
 	url := "https://foo.documents.azure.com:443/"
 	scope, err := createScopeFromEndpoint(url)
diff --git a/sdk/data/azcosmos/cosmos_container_test.go b/sdk/data/azcosmos/cosmos_container_test.go
index 7cb83c431c..f06d0da427 100644
--- a/sdk/data/azcosmos/cosmos_container_test.go
+++ b/sdk/data/azcosmos/cosmos_container_test.go
@@ -520,3 +520,72 @@ func TestContainerQueryItems(t *testing.T) {
 		}
 	}
 }
+
+func TestContainerExecuteBatch(t *testing.T) {
+	batchResponseRaw := []map[string]interface{}{
+		{"statusCode": 200, "requestCharge": 10.0, "eTag": "someETag", "resourceBody": "someBody"},
+		{"statusCode": 201, "requestCharge": 11.0, "eTag": "someETag2"},
+	}
+
+	jsonString, err := json.Marshal(batchResponseRaw)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	srv, close := mock.NewTLSServer()
+	defer close()
+	srv.SetResponse(
+		mock.WithBody(jsonString),
+		mock.WithStatusCode(http.StatusOK),
+		mock.WithHeader(cosmosHeaderEtag, "someEtag"),
+		mock.WithHeader(cosmosHeaderActivityId, "someActivityId"),
+		mock.WithHeader(cosmosHeaderRequestCharge, "13.42"))
+
+	verifier := pipelineVerifier{}
+
+	pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
+	client := &Client{endpoint: srv.URL(), pipeline: pl}
+
+	database, _ := newDatabase("databaseId", client)
+	container, _ := newContainer("containerId", database)
+
+	pk := NewPartitionKeyString("pk")
+	batch := container.NewTransactionalBatch(pk)
+	_, err = container.ExecuteTransactionalBatch(context.TODO(), batch, nil)
+	if err == nil {
+		t.Fatal("Expected error, but got nil")
+	}
+
+	batch.ReadItem("someId", nil)
+
+	body := map[string]string{
+		"foo": "bar",
+	}
+
+	itemMarshall, _ := json.Marshal(body)
+	batch.CreateItem(itemMarshall, nil)
+
+	_, err = container.ExecuteTransactionalBatch(context.TODO(), batch, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(verifier.requests) != 1 {
+		t.Fatalf("Expected 1 request, got %d", len(verifier.requests))
+	}
+
+	request := verifier.requests[0]
+
+	if request.method != http.MethodPost {
+		t.Errorf("Expected method to be %s, but got %s", http.MethodPost, request.method)
+	}
+
+	if request.url.RequestURI() != "/dbs/databaseId/colls/containerId/docs" {
+		t.Errorf("Expected url to be %s, but got %s", "/dbs/databaseId/colls/containerId/docs", request.url.RequestURI())
+	}
+
+	marshalledOperations, _ := json.Marshal(batch.operations)
+	if request.body != string(marshalledOperations) {
+		t.Errorf("Expected %v, but got %v", string(marshalledOperations), request.body)
+	}
+}
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch_options_test.go b/sdk/data/azcosmos/cosmos_transactional_batch_options_test.go
new file mode 100644
index 0000000000..cac1b51fa1
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch_options_test.go
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"testing"
+)
+
+func TestTransactionalBatchOptionsToHeaders(t *testing.T) {
+	options := &TransactionalBatchOptions{}
+	options.ConsistencyLevel = ConsistencyLevelSession.ToPtr()
+	options.SessionToken = "sessionToken"
+	header := options.toHeaders()
+	if header == nil {
+		t.Fatal("toHeaders should return non-nil")
+	}
+
+	headers := *header
+	if headers[cosmosHeaderConsistencyLevel] != "Session" {
+		t.Errorf("ConsistencyLevel should be Session but got %v", headers[cosmosHeaderConsistencyLevel])
+	}
+	if headers[cosmosHeaderSessionToken] != "sessionToken" {
+		t.Errorf("SessionToken should be sessionToken but got %v", headers[cosmosHeaderSessionToken])
+	}
+	if headers[cosmosHeaderIsBatchAtomic] != "True" {
+		t.Fatal("IsBatchAtomic should be true")
+	}
+	if headers[cosmosHeaderIsBatchRequest] != "True" {
+		t.Fatal("IsBatchRequest should be true")
+	}
+	if headers[cosmosHeaderIsBatchOrdered] != "True" {
+		t.Fatal("IsBatchOrdered should be true")
+	}
+}
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch_response_test.go b/sdk/data/azcosmos/cosmos_transactional_batch_response_test.go
new file mode 100644
index 0000000000..77f89fd8a5
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch_response_test.go
@@ -0,0 +1,144 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"context"
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
+	"github.com/Azure/azure-sdk-for-go/sdk/internal/mock"
+)
+
+func TestTransactionalBatchResponseParsing(t *testing.T) {
+	batchResponseRaw := []map[string]interface{}{
+		{"statusCode": 200, "requestCharge": 10.0, "eTag": "someETag", "resourceBody": "someBody"},
+		{"statusCode": 201, "requestCharge": 11.0, "eTag": "someETag2"},
+	}
+
+	jsonString, err := json.Marshal(batchResponseRaw)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	srv, close := mock.NewTLSServer()
+	defer close()
+	srv.SetResponse(
+		mock.WithBody(jsonString),
+		mock.WithStatusCode(http.StatusOK),
+		mock.WithHeader(cosmosHeaderEtag, "someEtag"),
+		mock.WithHeader(cosmosHeaderActivityId, "someActivityId"),
+		mock.WithHeader(cosmosHeaderRequestCharge, "13.42"))
+
+	req, err := azruntime.NewRequest(context.Background(), http.MethodGet, srv.URL())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
+	resp, _ := pl.Do(req)
+	parsedResponse, err := newTransactionalBatchResponse(resp)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !parsedResponse.Success {
+		t.Errorf("Expected Success to be true, but got false")
+	}
+
+	if parsedResponse.RawResponse == nil {
+		t.Fatal("parsedResponse.RawResponse is nil")
+	}
+
+	if parsedResponse.ActivityID != "someActivityId" {
+		t.Errorf("Expected ActivityId to be %s, but got %s", "someActivityId", parsedResponse.ActivityID)
+	}
+
+	if parsedResponse.RequestCharge != 13.42 {
+		t.Errorf("Expected RequestCharge to be %f, but got %f", 13.42, parsedResponse.RequestCharge)
+	}
+
+	if parsedResponse.ETag != "someEtag" {
+		t.Errorf("Expected ETag to be %s, but got %s", "someEtag", parsedResponse.ETag)
+	}
+
+	if len(parsedResponse.OperationResults) != 2 {
+		t.Errorf("Expected 2 documents, but got %d", len(parsedResponse.OperationResults))
+	}
+
+	for index, item := range parsedResponse.OperationResults {
+		if index == 0 && item.ETag != "someETag" {
+			t.Errorf("Expected ETag to be %s, but got %s", "someETag", item.ETag)
+		}
+
+		if index == 1 && item.ETag != "someETag2" {
+			t.Errorf("Expected ETag to be %s, but got %s", "someETag2", item.ETag)
+		}
+
+		if index == 0 && item.StatusCode != http.StatusOK {
+			t.Errorf("Expected StatusCode to be %d, but got %d", http.StatusOK, item.StatusCode)
+		}
+
+		if index == 1 && item.StatusCode != http.StatusCreated {
+			t.Errorf("Expected StatusCode to be %d, but got %d", http.StatusCreated, item.StatusCode)
+		}
+
+		if index == 0 && item.RequestCharge != 10 {
+			t.Errorf("Expected RequestCharge to be %f, but got %f", 10.0, item.RequestCharge)
+		}
+
+		if index == 1 && item.RequestCharge != 11 {
+			t.Errorf("Expected RequestCharge to be %f, but got %f", 11.0, item.RequestCharge)
+		}
+
+		if index == 0 && string(item.ResourceBody) != "\"someBody\"" {
+			t.Errorf("Expected ResourceBody to be %s, but got %s", "someBody", item.ResourceBody)
+		}
+
+		if index == 1 && item.ResourceBody != nil {
+			t.Errorf("Expected ResourceBody to be nil, but got %s", item.ResourceBody)
+		}
+
+	}
+}
+
+func TestTransactionalBatchResponseParsing_Failed(t *testing.T) {
+	batchResponseRaw := []map[string]interface{}{
+		{"statusCode": 424},
+		{"statusCode": 409},
+	}
+
+	jsonString, err := json.Marshal(batchResponseRaw)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	srv, close := mock.NewTLSServer()
+	defer close()
+	srv.SetResponse(
+		mock.WithBody(jsonString),
+		mock.WithStatusCode(http.StatusMultiStatus),
+		mock.WithHeader(cosmosHeaderEtag, "someEtag"),
+		mock.WithHeader(cosmosHeaderActivityId, "someActivityId"),
+		mock.WithHeader(cosmosHeaderRequestCharge, "13.42"))
+
+	req, err := azruntime.NewRequest(context.Background(), http.MethodGet, srv.URL())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
+	resp, _ := pl.Do(req)
+	parsedResponse, err := newTransactionalBatchResponse(resp)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if parsedResponse.Success {
+		t.Errorf("Expected Success to be false, but got true")
+	}
+}
diff --git a/sdk/data/azcosmos/cosmos_transactional_batch_test.go b/sdk/data/azcosmos/cosmos_transactional_batch_test.go
new file mode 100644
index 0000000000..84b03b59fa
--- /dev/null
+++ b/sdk/data/azcosmos/cosmos_transactional_batch_test.go
@@ -0,0 +1,176 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+)
+
+func TestTransactionalBatchCreateItem(t *testing.T) {
+	batch := &TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+	body := map[string]string{
+		"foo": "bar",
+	}
+
+	itemMarshall, _ := json.Marshal(body)
+	batch.CreateItem(itemMarshall, nil)
+
+	if len(batch.operations) != 1 {
+		t.Errorf("Expected 1 operation, but got %v", len(batch.operations))
+	}
+
+	if batch.operations[0].getOperationType() != operationTypeCreate {
+		t.Errorf("Expected operation type %v, but got %v", operationTypeCreate, batch.operations[0].getOperationType())
+	}
+
+	asCreate := batch.operations[0].(batchOperationCreate)
+
+	if asCreate.operationType != "Create" {
+		t.Errorf("Expected operation type %v, but got %v", "Create", asCreate.operationType)
+	}
+
+	if string(asCreate.resourceBody) != string(itemMarshall) {
+		t.Errorf("Expected body %v, but got %v", string(itemMarshall), string(asCreate.resourceBody))
+	}
+}
+
+func TestTransactionalBatchReadItem(t *testing.T) {
+	batch := &TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+	itemId := "bar"
+	batch.ReadItem(itemId, nil)
+
+	if len(batch.operations) != 1 {
+		t.Errorf("Expected 1 operation, but got %v", len(batch.operations))
+	}
+
+	if batch.operations[0].getOperationType() != operationTypeRead {
+		t.Errorf("Expected operation type %v, but got %v", operationTypeRead, batch.operations[0].getOperationType())
+	}
+
+	asRead := batch.operations[0].(batchOperationRead)
+
+	if asRead.operationType != "Read" {
+		t.Errorf("Expected operation type %v, but got %v", "Read", asRead.operationType)
+	}
+
+	if asRead.id != itemId {
+		t.Errorf("Expected id %v, but got %v", itemId, asRead.id)
+	}
+}
+
+func TestTransactionalBatchUpsertItem(t *testing.T) {
+	batch := &TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+	body := map[string]string{
+		"foo": "bar",
+	}
+
+	itemMarshall, _ := json.Marshal(body)
+
+	options := &TransactionalBatchItemOptions{}
+	etag := azcore.ETag("someEtag")
+	options.IfMatchETag = &etag
+	batch.UpsertItem(itemMarshall, options)
+
+	if len(batch.operations) != 1 {
+		t.Errorf("Expected 1 operation, but got %v", len(batch.operations))
+	}
+
+	if batch.operations[0].getOperationType() != operationTypeUpsert {
+		t.Errorf("Expected operation type %v, but got %v", operationTypeUpsert, batch.operations[0].getOperationType())
+	}
+
+	asUpsert := batch.operations[0].(batchOperationUpsert)
+
+	if asUpsert.operationType != "Upsert" {
+		t.Errorf("Expected operation type %v, but got %v", "Upsert", asUpsert.operationType)
+	}
+
+	if asUpsert.ifMatch != options.IfMatchETag {
+		t.Errorf("Expected ifMatch %v, but got %v", etag, asUpsert.ifMatch)
+	}
+
+	if string(asUpsert.resourceBody) != string(itemMarshall) {
+		t.Errorf("Expected body %v, but got %v", string(itemMarshall), string(asUpsert.resourceBody))
+	}
+}
+
+func TestTransactionalBatchReplaceItem(t *testing.T) {
+	batch := &TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+	body := map[string]string{
+		"foo": "bar",
+	}
+
+	itemMarshall, _ := json.Marshal(body)
+
+	options := &TransactionalBatchItemOptions{}
+	etag := azcore.ETag("someEtag")
+	options.IfMatchETag = &etag
+	itemId := "bar"
+	batch.ReplaceItem(itemId, itemMarshall, options)
+
+	if len(batch.operations) != 1 {
+		t.Errorf("Expected 1 operation, but got %v", len(batch.operations))
+	}
+
+	if batch.operations[0].getOperationType() != operationTypeReplace {
+		t.Errorf("Expected operation type %v, but got %v", operationTypeReplace, batch.operations[0].getOperationType())
+	}
+
+	asReplace := batch.operations[0].(batchOperationReplace)
+
+	if asReplace.operationType != "Replace" {
+		t.Errorf("Expected operation type %v, but got %v", "Replace", asReplace.operationType)
+	}
+
+	if asReplace.id != itemId {
+		t.Errorf("Expected id %v, but got %v", itemId, asReplace.id)
+	}
+
+	if asReplace.ifMatch != options.IfMatchETag {
+		t.Errorf("Expected ifMatch %v, but got %v", etag, asReplace.ifMatch)
+	}
+
+	if string(asReplace.resourceBody) != string(itemMarshall) {
+		t.Errorf("Expected body %v, but got %v", string(itemMarshall), string(asReplace.resourceBody))
+	}
+}
+
+func TestTransactionalBatchDeleteItem(t *testing.T) {
+	batch := &TransactionalBatch{}
+	batch.partitionKey = NewPartitionKeyString("foo")
+	options := &TransactionalBatchItemOptions{}
+	etag := azcore.ETag("someEtag")
+	options.IfMatchETag = &etag
+	itemId := "bar"
+	batch.DeleteItem(itemId, options)
+
+	if len(batch.operations) != 1 {
+		t.Errorf("Expected 1 operation, but got %v", len(batch.operations))
+	}
+
+	if batch.operations[0].getOperationType() != operationTypeDelete {
+		t.Errorf("Expected operation type %v, but got %v", operationTypeDelete, batch.operations[0].getOperationType())
+	}
+
+	asDelete := batch.operations[0].(batchOperationDelete)
+
+	if asDelete.operationType != "Delete" {
+		t.Errorf("Expected operation type %v, but got %v", "Delete", asDelete.operationType)
+	}
+
+	if asDelete.ifMatch != options.IfMatchETag {
+		t.Errorf("Expected ifMatch %v, but got %v", etag, asDelete.ifMatch)
+	}
+
+	if asDelete.id != itemId {
+		t.Errorf("Expected id %v, but got %v", itemId, asDelete.id)
+	}
+}
diff --git a/sdk/data/azcosmos/emulator_cosmos_batch_test.go b/sdk/data/azcosmos/emulator_cosmos_batch_test.go
new file mode 100644
index 0000000000..c44791ba5b
--- /dev/null
+++ b/sdk/data/azcosmos/emulator_cosmos_batch_test.go
@@ -0,0 +1,220 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azcosmos
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+func TestItemTransactionalBatch(t *testing.T) {
+	emulatorTests := newEmulatorTests(t)
+	client := emulatorTests.getClient(t)
+
+	database := emulatorTests.createDatabase(t, context.TODO(), client, "itemCRUD")
+	defer emulatorTests.deleteDatabase(t, context.TODO(), database)
+	properties := ContainerProperties{
+		ID: "aContainer",
+		PartitionKeyDefinition: PartitionKeyDefinition{
+			Paths: []string{"/pk"},
+		},
+	}
+
+	_, err := database.CreateContainer(context.TODO(), properties, nil)
+	if err != nil {
+		t.Fatalf("Failed to create container: %v", err)
+	}
+
+	container, _ := client.NewContainer("itemCRUD", "aContainer")
+
+	pkValue := NewPartitionKeyString("tBatch")
+	batch := container.NewTransactionalBatch(pkValue)
+
+	batch.CreateItem(emulatorTests.marshallItem("test", "tBatch"), nil)
+	batch.CreateItem(emulatorTests.marshallItem("test2", "tBatch"), nil)
+
+	// Default behavior has no content body
+	response, err := container.ExecuteTransactionalBatch(context.TODO(), batch, nil)
+	if err != nil {
+		t.Fatalf("Failed to execute batch: %v", err)
+	}
+
+	if len(response.OperationResults) != 2 {
+		t.Fatalf("Expected 2 operation results, got %v", len(response.OperationResults))
+	}
+
+	if !response.Success {
+		t.Fatalf("Expected committed to be true, got false")
+	}
+
+	for _, operationResult := range response.OperationResults {
+		if operationResult.StatusCode != http.StatusCreated {
+			t.Fatalf("Expected status code 201, got %v", operationResult.StatusCode)
+		}
+
+		if operationResult.RequestCharge <= 0 {
+			t.Fatalf("Expected RequestCharge to be greater than 0, got %v", operationResult.RequestCharge)
+		}
+
+		if operationResult.ETag == "" {
+			t.Fatalf("Expected ETag to be non-empty, got %v", operationResult.ETag)
+		}
+
+		if operationResult.ResourceBody != nil {
+			t.Fatalf("Expected ResourceBody to be nil, got %v", operationResult.ResourceBody)
+		}
+	}
+
+	batch2 := container.NewTransactionalBatch(pkValue)
+
+	batch2.CreateItem(emulatorTests.marshallItem("test3", "tBatch"), nil)
+	batch2.ReadItem("test2", nil)
+	batch2.DeleteItem("test", nil)
+
+	// If there is a read operation, body should be included
+	response2, err := container.ExecuteTransactionalBatch(context.TODO(), batch2, nil)
+	if err != nil {
+		t.Fatalf("Failed to execute batch: %v", err)
+	}
+
+	if !response2.Success {
+		t.Fatalf("Expected committed to be true, got false")
+	}
+
+	if len(response2.OperationResults) != 3 {
+		t.Fatalf("Expected 3 operation results, got %v", len(response2.OperationResults))
+	}
+
+	for index, operationResult := range response2.OperationResults {
+		if index == 0 && operationResult.StatusCode != http.StatusCreated {
+			t.Fatalf("Expected status code 201, got %v", operationResult.StatusCode)
+		}
+
+		if index == 1 && operationResult.StatusCode != http.StatusOK {
+			t.Fatalf("Expected status code 200, got %v", operationResult.StatusCode)
+		}
+
+		if index == 2 && operationResult.StatusCode != http.StatusNoContent {
+			t.Fatalf("Expected status code 204, got %v", operationResult.StatusCode)
+		}
+
+		if operationResult.RequestCharge <= 0 {
+			t.Fatalf("Expected RequestCharge to be greater than 0, got %v", operationResult.RequestCharge)
+		}
+
+		if index < 2 && operationResult.ETag == "" {
+			t.Fatalf("Expected ETag to be non-empty, got %v", operationResult.ETag)
+		}
+
+		if index < 2 && operationResult.ResourceBody == nil {
+			t.Fatalf("Expected ResourceBody to be not-nil, got %v", operationResult.ResourceBody)
+		}
+
+		if index == 2 && operationResult.ResourceBody != nil {
+			t.Fatalf("Expected ResourceBody to be nil, got %v", operationResult.ResourceBody)
+		}
+	}
+
+	// Forcing body through options
+	batch3 := container.NewTransactionalBatch(pkValue)
+
+	batch3.UpsertItem(emulatorTests.marshallItem("test4", "tBatch"), nil)
+	batch3.ReplaceItem("test3", emulatorTests.marshallItem("test3", "tBatch"), nil)
+
+	response3, err := container.ExecuteTransactionalBatch(context.TODO(), batch3, &TransactionalBatchOptions{EnableContentResponseOnWrite: true})
+	if err != nil {
+		t.Fatalf("Failed to execute batch: %v", err)
+	}
+
+	if !response3.Success {
+		t.Fatalf("Expected Success to be true, got false")
+	}
+
+	if len(response3.OperationResults) != 2 {
+		t.Fatalf("Expected 2 operation results, got %v", len(response3.OperationResults))
+	}
+
+	for index, operationResult := range response3.OperationResults {
+		if index == 0 && operationResult.StatusCode != http.StatusCreated {
+			t.Fatalf("Expected status code 201, got %v", operationResult.StatusCode)
+		}
+
+		if index == 1 && operationResult.StatusCode != http.StatusOK {
+			t.Fatalf("Expected status code 200, got %v", operationResult.StatusCode)
+		}
+
+		if operationResult.RequestCharge <= 0 {
+			t.Fatalf("Expected RequestCharge to be greater than 0, got %v", operationResult.RequestCharge)
+		}
+
+		if operationResult.ETag == "" {
+			t.Fatalf("Expected ETag to be non-empty, got %v", operationResult.ETag)
+		}
+
+		if operationResult.ResourceBody == nil {
+			t.Fatalf("Expected ResourceBody not to be nil, got %v", operationResult.ResourceBody)
+		}
+	}
+}
+
+func TestItemTransactionalBatchError(t *testing.T) {
+	emulatorTests := newEmulatorTests(t)
+	client := emulatorTests.getClient(t)
+
+	database := emulatorTests.createDatabase(t, context.TODO(), client, "itemCRUD")
+	defer emulatorTests.deleteDatabase(t, context.TODO(), database)
+	properties := ContainerProperties{
+		ID: "aContainer",
+		PartitionKeyDefinition: PartitionKeyDefinition{
+			Paths: []string{"/pk"},
+		},
+	}
+
+	_, err := database.CreateContainer(context.TODO(), properties, nil)
+	if err != nil {
+		t.Fatalf("Failed to create container: %v", err)
+	}
+
+	container, _ := client.NewContainer("itemCRUD", "aContainer")
+
+	pkValue := NewPartitionKeyString("tBatch")
+
+	_, err = container.CreateItem(context.TODO(), pkValue, emulatorTests.marshallItem("test", "tBatch"), nil)
+	if err != nil {
+		t.Fatalf("Failed to create item: %v", err)
+	}
+
+	batch := container.NewTransactionalBatch(pkValue)
+
+	batch.CreateItem(emulatorTests.marshallItem("test2", "tBatch"), nil)
+	batch.CreateItem(emulatorTests.marshallItem("test", "tBatch"), nil)
+
+	response, err := container.ExecuteTransactionalBatch(context.TODO(), batch, &TransactionalBatchOptions{EnableContentResponseOnWrite: true})
+	if err != nil {
+		t.Fatalf("Failed to execute batch: %v", err)
+	}
+
+	if response.RawResponse.StatusCode != http.StatusMultiStatus {
+		t.Fatalf("Expected status code 207, got %v", response.RawResponse.StatusCode)
+	}
+
+	if response.Success {
+		t.Fatalf("Expected Success to be false, got true")
+	}
+
+	if len(response.OperationResults) != 2 {
+		t.Fatalf("Expected 2 operation results, got %v", len(response.OperationResults))
+	}
+
+	for index, operationResult := range response.OperationResults {
+		if index == 0 && operationResult.StatusCode != http.StatusFailedDependency {
+			t.Fatalf("Expected status code 424, got %v", operationResult.StatusCode)
+		}
+
+		if index == 1 && operationResult.StatusCode != http.StatusConflict {
+			t.Fatalf("Expected status code 409, got %v", operationResult.StatusCode)
+		}
+	}
+}
diff --git a/sdk/data/azcosmos/emulator_tests.go b/sdk/data/azcosmos/emulator_tests.go
index c8994ab939..d36c2a19fc 100644
--- a/sdk/data/azcosmos/emulator_tests.go
+++ b/sdk/data/azcosmos/emulator_tests.go
@@ -6,6 +6,7 @@ package azcosmos
 import (
 	"context"
 	"encoding/base64"
+	"encoding/json"
 	"os"
 	"strconv"
 	"testing"
@@ -81,6 +82,16 @@ func (e *emulatorTests) deleteDatabase(
 	}
 }
 
+func (e *emulatorTests) marshallItem(id string, pk string) []byte {
+	item := map[string]string{
+		"id": id,
+		"pk": pk,
+	}
+
+	marshalled, _ := json.Marshal(item)
+	return marshalled
+}
+
 type emulatorTokenCredential struct {
 }
 
diff --git a/sdk/data/azcosmos/example_test.go b/sdk/data/azcosmos/example_test.go
index 32d5161468..79611af665 100644
--- a/sdk/data/azcosmos/example_test.go
+++ b/sdk/data/azcosmos/example_test.go
@@ -8,6 +8,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"net/http"
 	"os"
 	"time"
 
@@ -600,6 +601,80 @@ func ExampleContainerClient_NewQueryItemsPager() {
 	}
 }
 
+func ExampleContainerClient_NewTransactionalBatch() {
+	endpoint, ok := os.LookupEnv("AZURE_COSMOS_ENDPOINT")
+	if !ok {
+		panic("AZURE_COSMOS_ENDPOINT could not be found")
+	}
+
+	key, ok := os.LookupEnv("AZURE_COSMOS_KEY")
+	if !ok {
+		panic("AZURE_COSMOS_KEY could not be found")
+	}
+
+	cred, err := azcosmos.NewKeyCredential(key)
+	if err != nil {
+		panic(err)
+	}
+
+	client, err := azcosmos.NewClientWithKey(endpoint, cred, nil)
+	if err != nil {
+		panic(err)
+	}
+
+	container, err := client.NewContainer("databaseName", "aContainer")
+	if err != nil {
+		panic(err)
+	}
+
+	pk := azcosmos.NewPartitionKeyString("newPartitionKey")
+
+	batch := container.NewTransactionalBatch(pk)
+
+	item := map[string]string{
+		"id":             "anId",
+		"value":          "2",
+		"myPartitionKey": "newPartitionKey",
+	}
+
+	marshalledItem, err := json.Marshal(item)
+	if err != nil {
+		panic(err)
+	}
+
+	batch.CreateItem(marshalledItem, nil)
+	batch.ReadItem("anIdThatExists", nil)
+	batch.DeleteItem("yetAnotherExistingId", nil)
+
+	batchResponse, err := container.ExecuteTransactionalBatch(context.Background(), batch, nil)
+	if err != nil {
+		panic(err)
+	}
+
+	if batchResponse.Success {
+		// Transaction succeeded
+		// We can inspect the individual operation results
+		for index, operation := range batchResponse.OperationResults {
+			fmt.Printf("Operation %v completed with status code %v consumed %v RU", index, operation.StatusCode, operation.RequestCharge)
+			if index == 1 {
+				// Read operation would have body available
+				var itemResponseBody map[string]string
+				err = json.Unmarshal(operation.ResourceBody, &itemResponseBody)
+				if err != nil {
+					panic(err)
+				}
+			}
+		}
+	} else {
+		// Transaction failed, look for the offending operation
+		for index, operation := range batchResponse.OperationResults {
+			if operation.StatusCode != http.StatusFailedDependency {
+				fmt.Printf("Transaction failed due to operation %v which failed with status code %v", index, operation.StatusCode)
+			}
+		}
+	}
+}
+
 func retryOptimisticConcurrency(retryAttempts int, wait time.Duration, retry func() (bool, error)) (result error) {
 	for i := 0; ; i++ {
 		retryResult, err := retry()
diff --git a/sdk/keyvault/azkeys/client_test.go b/sdk/keyvault/azkeys/client_test.go
index da5403135b..8ef20b0feb 100644
--- a/sdk/keyvault/azkeys/client_test.go
+++ b/sdk/keyvault/azkeys/client_test.go
@@ -500,17 +500,28 @@ func TestUpdateKeyPropertiesImmutable(t *testing.T) {
 			})
 			require.NoError(t, err)
 
-			createResp, err := client.CreateRSAKey(ctx, key, &CreateRSAKeyOptions{
-				HardwareProtected: to.Ptr(true),
-				Properties: &Properties{
-					Exportable: to.Ptr(true),
-				},
-				ReleasePolicy: &ReleasePolicy{
-					Immutable:     to.Ptr(true),
-					EncodedPolicy: marshalledPolicy,
-				},
-				Operations: []*Operation{to.Ptr(OperationEncrypt), to.Ptr(OperationDecrypt)},
-			})
+			// retry creating the release policy because Key Vault sometimes can't reach
+			// the fake attestation service we use in CI for several minutes after deployment
+			var createResp CreateRSAKeyResponse
+			for i := 0; i < 5; i++ {
+				createResp, err = client.CreateRSAKey(ctx, key, &CreateRSAKeyOptions{
+					HardwareProtected: to.Ptr(true),
+					Properties: &Properties{
+						Exportable: to.Ptr(true),
+					},
+					ReleasePolicy: &ReleasePolicy{
+						Immutable:     to.Ptr(true),
+						EncodedPolicy: marshalledPolicy,
+					},
+					Operations: []*Operation{to.Ptr(OperationEncrypt), to.Ptr(OperationDecrypt)},
+				})
+				if err == nil {
+					break
+				}
+				if recording.GetRecordMode() != recording.PlaybackMode {
+					time.Sleep(time.Minute)
+				}
+			}
 			require.NoError(t, err)
 			defer cleanUpKey(t, client, key)
 
diff --git a/sdk/messaging/azservicebus/admin_client_test.go b/sdk/messaging/azservicebus/admin_client_test.go
index 5ca6818dbb..56e3947dc2 100644
--- a/sdk/messaging/azservicebus/admin_client_test.go
+++ b/sdk/messaging/azservicebus/admin_client_test.go
@@ -75,9 +75,7 @@ func TestAdminClient_Queue_Forwarding(t *testing.T) {
 	forwardedMessages, err := receiver.ReceiveMessages(context.Background(), 1, nil)
 	require.NoError(t, err)
 
-	body, err := forwardedMessages[0].Body()
-	require.NoError(t, err)
-	require.EqualValues(t, "this message will be auto-forwarded", string(body))
+	require.EqualValues(t, "this message will be auto-forwarded", string(forwardedMessages[0].Body))
 }
 
 func TestAdminClient_GetQueueRuntimeProperties(t *testing.T) {
diff --git a/sdk/messaging/azservicebus/client_test.go b/sdk/messaging/azservicebus/client_test.go
index 7ce444d41b..7628314f53 100644
--- a/sdk/messaging/azservicebus/client_test.go
+++ b/sdk/messaging/azservicebus/client_test.go
@@ -120,9 +120,7 @@ func TestNewClientWithWebsockets(t *testing.T) {
 	messages, err := receiver.ReceiveMessages(context.Background(), 1, nil)
 	require.NoError(t, err)
 
-	bytes, err := messages[0].Body()
-	require.NoError(t, err)
-	require.EqualValues(t, "hello world", string(bytes))
+	require.EqualValues(t, "hello world", string(messages[0].Body))
 }
 
 func TestNewClientUsingSharedAccessSignature(t *testing.T) {
@@ -154,9 +152,7 @@ func TestNewClientUsingSharedAccessSignature(t *testing.T) {
 	messages, err := receiver.ReceiveMessages(context.Background(), 1, nil)
 	require.NoError(t, err)
 
-	bytes, err := messages[0].Body()
-	require.NoError(t, err)
-	require.EqualValues(t, "hello world", string(bytes))
+	require.EqualValues(t, "hello world", string(messages[0].Body))
 }
 
 const fastNotFoundDuration = 10 * time.Second
diff --git a/sdk/messaging/azservicebus/example_receiver_test.go b/sdk/messaging/azservicebus/example_receiver_test.go
index 203f6fdf35..9b8ae0a9af 100644
--- a/sdk/messaging/azservicebus/example_receiver_test.go
+++ b/sdk/messaging/azservicebus/example_receiver_test.go
@@ -88,6 +88,11 @@ func ExampleReceiver_ReceiveMessages() {
 	}
 
 	for _, message := range messages {
+		// The message body is a []byte. For this example we're just assuming that the body
+		// was a string, converted to bytes but any []byte payload is valid.
+		var body []byte = message.Body
+		fmt.Printf("Message received with body: %s\n", string(body))
+
 		// For more information about settling messages:
 		// https://docs.microsoft.com/azure/service-bus-messaging/message-transfers-locks-settlement#settling-receive-operations
 		err = receiver.CompleteMessage(context.TODO(), message, nil)
diff --git a/sdk/messaging/azservicebus/internal/amqpLinks_test.go b/sdk/messaging/azservicebus/internal/amqpLinks_test.go
index bc0c35c4e5..445d437140 100644
--- a/sdk/messaging/azservicebus/internal/amqpLinks_test.go
+++ b/sdk/messaging/azservicebus/internal/amqpLinks_test.go
@@ -33,7 +33,7 @@ func (pe fakeNetError) Timeout() bool   { return pe.timeout }
 func (pe fakeNetError) Temporary() bool { return pe.temp }
 func (pe fakeNetError) Error() string   { return "Fake but very permanent error" }
 
-func assertFailedLinks(t *testing.T, lwid *LinksWithID, expectedErr error) {
+func assertFailedLinks(t *testing.T, lwid *LinksWithID, expectedErr error, expectedRPCError error) {
 	err := lwid.Sender.Send(context.TODO(), &amqp.Message{
 		Data: [][]byte{
 			{0},
@@ -42,7 +42,7 @@ func assertFailedLinks(t *testing.T, lwid *LinksWithID, expectedErr error) {
 	require.ErrorIs(t, err, expectedErr)
 
 	_, err = PeekMessages(context.TODO(), lwid.RPC, 0, 1)
-	require.ErrorIs(t, err, expectedErr)
+	require.ErrorIs(t, err, expectedRPCError)
 
 	msg, err := lwid.Receiver.Receive(context.TODO())
 	require.ErrorIs(t, err, expectedErr)
@@ -138,7 +138,7 @@ func TestAMQPLinksLive(t *testing.T) {
 	require.NoError(t, amqpClient.Close())
 
 	// all the links are dead because the connection is dead.
-	assertFailedLinks(t, lwr, amqp.ErrConnClosed)
+	assertFailedLinks(t, lwr, amqp.ErrConnClosed, amqp.ErrConnClosed)
 
 	// now we'll recover, which should recreate everything
 	require.NoError(t, links.RecoverIfNeeded(context.Background(), lwr.ID, amqp.ErrConnClosed))
@@ -156,7 +156,7 @@ func TestAMQPLinksLive(t *testing.T) {
 	_ = actualLinks.Receiver.Close(context.Background())
 	_ = actualLinks.RPCLink.Close(context.Background())
 
-	assertFailedLinks(t, lwr, amqp.ErrLinkClosed)
+	assertFailedLinks(t, lwr, amqp.ErrLinkClosed, context.Canceled)
 
 	lwr, err = links.Get(context.Background())
 	require.NoError(t, err)
@@ -621,7 +621,7 @@ func TestAMQPLinksRetriesUnit(t *testing.T) {
 
 			var attempts []int32
 
-			err := links.Retry(context.Background(), log.Event("NotUsed"), "NotUsed", func(ctx context.Context, lwid *LinksWithID, args *utils.RetryFnArgs) error {
+			err := links.Retry(context.Background(), log.Event("NotUsed"), "OverallOperation", func(ctx context.Context, lwid *LinksWithID, args *utils.RetryFnArgs) error {
 				attempts = append(attempts, args.I)
 				return testData.Err
 			}, exported.RetryOptions{
@@ -634,7 +634,7 @@ func TestAMQPLinksRetriesUnit(t *testing.T) {
 			logMessages := endLogging()
 
 			if testData.ExpectReset {
-				require.Contains(t, logMessages, fmt.Sprintf("[azsb.Conn] Link was previously detached. Attempting quick reconnect to recover from error: %s", err.Error()))
+				require.Contains(t, logMessages, fmt.Sprintf("[azsb.Conn] (OverallOperation) Link was previously detached. Attempting quick reconnect to recover from error: %s", err.Error()))
 			} else {
 				for _, msg := range logMessages {
 					require.NotContains(t, msg, "Link was previously detached")
diff --git a/sdk/messaging/azservicebus/message_test.go b/sdk/messaging/azservicebus/message_test.go
index 70d360f9af..28668d2178 100644
--- a/sdk/messaging/azservicebus/message_test.go
+++ b/sdk/messaging/azservicebus/message_test.go
@@ -60,6 +60,9 @@ func TestAMQPMessageToReceivedMessage(t *testing.T) {
 		scheduledEnqueueTime := time.Now().Add(3 * time.Hour)
 
 		amqpMessage := &amqp.Message{
+			Data: [][]byte{
+				[]byte("hello"),
+			},
 			Annotations: map[interface{}]interface{}{
 				"x-opt-locked-until":            lockedUntil,
 				"x-opt-sequence-number":         int64(101),
@@ -72,6 +75,7 @@ func TestAMQPMessageToReceivedMessage(t *testing.T) {
 
 		receivedMessage := newReceivedMessage(amqpMessage)
 
+		require.Equal(t, []byte("hello"), receivedMessage.Body)
 		require.EqualValues(t, lockedUntil, *receivedMessage.LockedUntil)
 		require.EqualValues(t, int64(101), *receivedMessage.SequenceNumber)
 		require.EqualValues(t, "partitionKey1", *receivedMessage.PartitionKey)
@@ -143,9 +147,7 @@ func TestAMQPMessageToMessage(t *testing.T) {
 	require.EqualValues(t, msg.To, amqpMsg.Properties.To, "to")
 	require.EqualValues(t, MessageStateActive, msg.State)
 
-	body, err := msg.Body()
-	require.NoError(t, err)
-	require.EqualValues(t, body, amqpMsg.Data[0], "data")
+	require.EqualValues(t, msg.Body, amqpMsg.Data[0], "data")
 
 	expectedAMQPEncodedLockTokenGUID := [16]byte{187, 49, 89, 205, 253, 254, 205, 77, 162, 38, 172, 76, 45, 235, 91, 225}
 
@@ -189,3 +191,28 @@ func TestMessageState(t *testing.T) {
 		require.EqualValues(t, MessageStateActive, m.State)
 	})
 }
+
+func TestMessageWithIncorrectBody(t *testing.T) {
+	// these are cases where the simple ReceivedMessage can't represent the AMQP message's
+	// payload.
+	message := newReceivedMessage(&amqp.Message{})
+	require.Nil(t, message.Body)
+
+	message = newReceivedMessage(&amqp.Message{
+		Value: "hello",
+	})
+	require.Nil(t, message.Body)
+
+	message = newReceivedMessage(&amqp.Message{
+		Sequence: [][]any{},
+	})
+	require.Nil(t, message.Body)
+
+	message = newReceivedMessage(&amqp.Message{
+		Data: [][]byte{
+			[]byte("hello"),
+			[]byte("world"),
+		},
+	})
+	require.Nil(t, message.Body)
+}
diff --git a/sdk/messaging/azservicebus/receiver_test.go b/sdk/messaging/azservicebus/receiver_test.go
index c0608a47af..fd55b3d203 100644
--- a/sdk/messaging/azservicebus/receiver_test.go
+++ b/sdk/messaging/azservicebus/receiver_test.go
@@ -60,12 +60,9 @@ func TestReceiverSendFiveReceiveFive(t *testing.T) {
 	require.EqualValues(t, 5, len(messages))
 
 	for i := 0; i < 5; i++ {
-		body, err := messages[i].Body()
-		require.NoError(t, err)
-
 		require.EqualValues(t,
 			fmt.Sprintf("[%d]: send five, receive five", i),
-			string(body))
+			string(messages[i].Body))
 
 		require.NoError(t, receiver.CompleteMessage(context.Background(), messages[i], nil))
 	}
@@ -380,11 +377,8 @@ func TestReceiverPeek(t *testing.T) {
 	require.NoError(t, err)
 	require.EqualValues(t, 1, len(repeekedMessages))
 
-	body, err := peekedMessages2[0].Body()
-	require.NoError(t, err)
-
 	require.EqualValues(t, []string{
-		string(body),
+		string(peekedMessages2[0].Body),
 	}, getSortedBodies(repeekedMessages))
 
 	// and peek again (note it won't reset so there'll be "nothing")
@@ -789,19 +783,7 @@ func (messages receivedMessageSlice) Len() int {
 }
 
 func (messages receivedMessageSlice) Less(i, j int) bool {
-	bodyI, err := messages[i].Body()
-
-	if err != nil {
-		panic(err)
-	}
-
-	bodyJ, err := messages[j].Body()
-
-	if err != nil {
-		panic(err)
-	}
-
-	return string(bodyI) < string(bodyJ)
+	return string(messages[i].Body) < string(messages[j].Body)
 }
 
 func (messages receivedMessageSlice) Swap(i, j int) {
diff --git a/sdk/messaging/azservicebus/sender_test.go b/sdk/messaging/azservicebus/sender_test.go
index 36abb705fb..67154510a4 100644
--- a/sdk/messaging/azservicebus/sender_test.go
+++ b/sdk/messaging/azservicebus/sender_test.go
@@ -198,9 +198,7 @@ func Test_Sender_SendMessages_resend(t *testing.T) {
 		require.NoError(t, err)
 
 		require.EqualValues(t, "first send", messages[0].ApplicationProperties["Status"])
-		body, err := messages[0].Body()
-		require.NoError(t, err)
-		require.EqualValues(t, "ResendableMessage", string(body))
+		require.EqualValues(t, "ResendableMessage", string(messages[0].Body))
 
 		if complete {
 			require.NoError(t, receiver.CompleteMessage(ctx, messages[0], nil))
@@ -215,9 +213,7 @@ func Test_Sender_SendMessages_resend(t *testing.T) {
 		messages, err = receiver.ReceiveMessages(ctx, 1, nil)
 		require.NoError(t, err)
 		require.EqualValues(t, "resend", messages[0].ApplicationProperties["Status"])
-		body, err = messages[0].Body()
-		require.NoError(t, err)
-		require.EqualValues(t, "ResendableMessage", string(body))
+		require.EqualValues(t, "ResendableMessage", string(messages[0].Body))
 
 		if complete {
 			require.NoError(t, receiver.CompleteMessage(ctx, messages[0], nil))
@@ -229,14 +225,11 @@ func Test_Sender_SendMessages_resend(t *testing.T) {
 }
 
 func messageFromReceivedMessage(t *testing.T, receivedMessage *ReceivedMessage) *Message {
-	body, err := receivedMessage.Body()
-	require.NoError(t, err)
-
 	newMsg := &Message{
 		MessageID:             &receivedMessage.MessageID,
 		ContentType:           receivedMessage.ContentType,
 		CorrelationID:         receivedMessage.CorrelationID,
-		Body:                  body,
+		Body:                  receivedMessage.Body,
 		SessionID:             receivedMessage.SessionID,
 		Subject:               receivedMessage.Subject,
 		ReplyTo:               receivedMessage.ReplyTo,
@@ -430,13 +423,7 @@ func getSortedBodies(messages []*ReceivedMessage) []string {
 	var bodies []string
 
 	for _, msg := range messages {
-		body, err := msg.Body()
-
-		if err != nil {
-			panic(err)
-		}
-
-		bodies = append(bodies, string(body))
+		bodies = append(bodies, string(msg.Body))
 	}
 
 	return bodies
@@ -450,19 +437,7 @@ func (rm receivedMessages) Len() int {
 
 // Less compares the messages assuming the .Body field is a valid string.
 func (rm receivedMessages) Less(i, j int) bool {
-	bodyI, err := rm[i].Body()
-
-	if err != nil {
-		panic(err)
-	}
-
-	bodyJ, err := rm[j].Body()
-
-	if err != nil {
-		panic(err)
-	}
-
-	return string(bodyI) < string(bodyJ)
+	return string(rm[i].Body) < string(rm[j].Body)
 }
 
 func (rm receivedMessages) Swap(i, j int) {
diff --git a/sdk/messaging/azservicebus/session_receiver_test.go b/sdk/messaging/azservicebus/session_receiver_test.go
index 84563b6d22..cfc8348933 100644
--- a/sdk/messaging/azservicebus/session_receiver_test.go
+++ b/sdk/messaging/azservicebus/session_receiver_test.go
@@ -43,10 +43,7 @@ func TestSessionReceiver_acceptSession(t *testing.T) {
 	messages, err := receiver.inner.ReceiveMessages(ctx, 1, nil)
 	require.NoError(t, err)
 
-	body, err := messages[0].Body()
-	require.NoError(t, err)
-
-	require.EqualValues(t, "session-based message", body)
+	require.EqualValues(t, "session-based message", messages[0].Body)
 	require.EqualValues(t, "session-1", *messages[0].SessionID)
 	require.NoError(t, receiver.CompleteMessage(ctx, messages[0], nil))
 
@@ -112,11 +109,8 @@ func TestSessionReceiver_blankSessionIDs(t *testing.T) {
 	}
 
 	for _, msg := range received {
-		body, err := msg.Body()
-		require.NoError(t, err)
 		require.EqualValues(t, "", *msg.SessionID)
-
-		require.EqualValues(t, "session-based message", string(body))
+		require.EqualValues(t, "session-based message", string(msg.Body))
 	}
 }
 
@@ -165,9 +159,7 @@ func TestSessionReceiver_acceptNextSession(t *testing.T) {
 	messages, err := receiver.inner.ReceiveMessages(ctx, 1, nil)
 	require.NoError(t, err)
 
-	body, err := messages[0].Body()
-	require.NoError(t, err)
-	require.EqualValues(t, "session-based message", body)
+	require.EqualValues(t, "session-based message", messages[0].Body)
 	require.EqualValues(t, "acceptnextsession-test", *messages[0].SessionID)
 	require.NoError(t, receiver.CompleteMessage(ctx, messages[0], nil))