Skip to content

Copy-AzStorageBlob with OAuth context may generate source user delegation SAS that is not yet valid #29774

Description

@sherryhao

Description

When using Copy-AzStorageBlob with an OAuth storage context, the cmdlet internally calls GetUserDelegationKey and generates a user delegation SAS for the source blob URL.

This is expected at a high level because Copy Blob From URL requires the source URL to be public or authorized with SAS. However, the generated user delegation SAS appears to use a start time very close to the current request time.

In a customer scenario, the source access intermittently failed with:

AuthenticationFailed
Signature not valid in the specified key time frame

The customer did not explicitly create a SAS. Their script only used an OAuth context and Copy-AzStorageBlob:

Connect-AzAccount -Identity -Environment AzureChinaCloud -Tenant "<tenant-id>"

$ctx = New-AzStorageContext -StorageAccountName "<account-name>"

Copy-AzStorageBlob `
    -Context $ctx `
    -SrcContainer "<src-container>" `
    -SrcBlob "<blob-name>" `
    -DestContainer "<dest-container>" `
    -DestBlob "<blob-name>" `
    -Force

We also validated this behavior with a sanitized test account using Az.Storage 8.2.0. With New-AzStorageContext -UseConnectedAccount, Copy-AzStorageBlob -Debug shows:

POST https://<account>.blob.core.<cloud-suffix>/?restype=service&comp=userdelegationkey
Response 200 OK

HEAD https://<account>.blob.core.<cloud-suffix>/<src-container>/<blob>
?skoid=<redacted>
&sktid=<redacted>
&skt=<timestamp>
&ske=<timestamp>
&sks=b
&skv=<version>
&sv=<version>
&se=<timestamp>
&sr=b
&sp=rt
&sig=<redacted>

So the source blob access is performed with an internally generated user delegation SAS even though the user script did not explicitly create a SAS.

Expected behavior:
Copy-AzStorageBlob should avoid generating a user delegation SAS whose start time is too close to the current time, or should retry when the service returns this specific boundary error:

AuthenticationFailed
Signature not valid in the specified key time frame

Potential options:

  • Backdate the generated user delegation SAS start time.
  • Avoid setting a SAS start time when possible.
  • Retry once after a short delay for this specific error.

This would align with Azure Storage SAS best practice guidance, which warns that setting SAS start time to the current time can cause intermittent failures due to clock skew.

Issue script & Debug output

Sanitized repro script:


$DebugPreference = 'Continue'

Connect-AzAccount -Environment AzureChinaCloud

$resourceGroupName = "<resource-group>"
$storageAccountName = "<storage-account>"
$srcContainer = "src"
$dstContainer = "dst"
$blobName = "test-copy.txt"

$ctx = New-AzStorageContext `
    -StorageAccountName $storageAccountName `
    -UseConnectedAccount `
    -Environment AzureChinaCloud

New-AzStorageContainer -Name $srcContainer -Context $ctx -ErrorAction SilentlyContinue
New-AzStorageContainer -Name $dstContainer -Context $ctx -ErrorAction SilentlyContinue

"hello" | Set-Content "$env:TEMP\$blobName"

Set-AzStorageBlobContent `
    -File "$env:TEMP\$blobName" `
    -Container $srcContainer `
    -Blob $blobName `
    -Context $ctx `
    -Force

Copy-AzStorageBlob `
    -Context $ctx `
    -SrcContainer $srcContainer `
    -SrcBlob $blobName `
    -DestContainer $dstContainer `
    -DestBlob $blobName `
    -Force `
    -Debug


Sanitized debug output excerpt:


CopyAzureStorageBlob begin processing with ParameterSet 'ContainerName'.
Use storage account '<account>' from storage context.

Request [<client-request-id>] HEAD https://<account>.blob.core.<cloud-suffix>/<src-container>/<blob-name>
Authorization: REDACTED
Response [<client-request-id>] 200 OK

Request [<client-request-id>] POST https://<account>.blob.core.<cloud-suffix>/?restype=service&comp=userdelegationkey
Authorization: REDACTED
Response [<client-request-id>] 200 OK

Request [<client-request-id>] HEAD https://<account>.blob.core.<cloud-suffix>/<src-container>/<blob-name>?skoid=<redacted>&sktid=<redacted>&skt=<timestamp>&ske=<timestamp>&sks=b&skv=<version>&sv=<version>&se=<timestamp>&sr=b&sp=rt&sig=REDACTED
Response [<client-request-id>] 200 OK

Request [<client-request-id>] PUT https://<account>.blob.core.<cloud-suffix>/<dst-container>/<blob-name>
x-ms-copy-source: REDACTED
Authorization: REDACTED
Response [<client-request-id>] 202 Accepted
x-ms-copy-status: success


The repro above demonstrates that `Copy-AzStorageBlob` internally calls `GetUserDelegationKey` and uses a generated user delegation SAS for the source blob URL when the context is OAuth-based.

The intermittent failure observed in the customer scenario was:


AuthenticationFailed
Signature not valid in the specified key time frame


The service-side details are available internally if needed, but are not included here because they contain customer/environment-specific information.

Environment data

Name                           Value
----                           -----
PSVersion                      7.6.2
PSEdition                      Core
OS                             Microsoft Windows 10.0.26200
Platform                       Win32NT

Module versions

Name        Version
----        -------
Az.Accounts 4.0.2
Az.Storage  8.2.0

Error output

No local `Resolve-AzError` output is available for the sanitized repro because the repro completed successfully.

The customer-observed intermittent service error was:


AuthenticationFailed
Signature not valid in the specified key time frame

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue requires a change to an existing behavior in the product in order to be resolved.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions