无法在 Azure 中使用系统分配的托管标识来使用 blob 存储操作
Not able to use blob storage operations using System Assigned Managed Identity in Azure
尝试使用 C# 语言在 Azure 中使用系统分配的托管标识时出现 "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature." 错误。
遵循的步骤
- 创建了一个启用身份的新虚拟机(系统分配)
- 使用存储帐户中的角色分配在 IAM 中添加了 VM
- 能够使用 C# 生成令牌
- 但是在读取 blob 时出现异常,以下是异常详细信息
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
at Microsoft.Azure.Storage.Core.Executor.Executor.<ExecuteAsync>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.Storage.Blob.CloudBlockBlob.<DownloadTextAsync>d__72.MoveNext()
计划Class
class Program
{
static void Main(string[] args)
{
try
{
var blob = new AzureCloudBlob();
Console.WriteLine(blob.ReadBlob());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
AzureCloudBlob class,用于使用系统分配的托管标识访问令牌连接和读取 blob
class AzureCloudBlob
{
public CloudBlockBlob CreateConnection()
{
var token = GetToken();
var tokenCredentials = new TokenCredential(token);
var storageCredentials = new StorageCredentials(tokenCredentials);
var serviceUri = new Uri("https://mystorageacc.blob.core.windows.net/practice/blob.txt");
return new CloudBlockBlob(serviceUri, storageCredentials);
}
public string ReadBlob()
{
var client = CreateConnection();
var blobClient = client.DownloadTextAsync();
var data = blobClient.Result;
return data;
}
public string GetToken()
{
var request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/");
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var list = (Dictionary<string, string>)JsonConvert.DeserializeObject(stringResponse, typeof(Dictionary<string, string>));
string accessToken = list["access_token"];
Console.WriteLine(accessToken);
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
Console.WriteLine(errorText);
return errorText;
}
}
}
如果您想使用 Azure AD 身份验证访问 Azure 存储,我们应该使用 resouce=https://storage.azure.com/
来获取 Azure AD 访问令牌。但是你用resource=https://management.azure.com/
。请更换它。此外,请注意您需要为 MSI 分配正确的角色。角色应该是 Storage Blob Data Reader、Storage Blob Data Contributor 或 Storage Blob Data Owner.
详细步骤如下
在 Azure VM 上启用系统分配的托管标识
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM
Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
在存储帐户范围内将角色分配给 MSI
$sp =Get-AzADServicePrincipal -displayname "<your VM name>"
New-AzRoleAssignment -ObjectId $sp.id `
-RoleDefinitionName "Storage Blob Data Reader" `
-Scope "/subscriptions/<subscription>/resourceGroups/sample-resource-group/providers/Microsoft.Storage/storageAccounts/<storage-account>"
代码
class Program
{
static void Main(string[] args)
{
//get token
string accessToken = GetMSIToken("https://storage.azure.com/");
//create token credential
TokenCredential tokenCredential = new TokenCredential(accessToken);
//create storage credentials
StorageCredentials storageCredentials = new StorageCredentials(tokenCredential);
Uri blobAddress = new Uri("<URI to blob file>");
//create block blob using storage credentials
CloudBlockBlob blob = new CloudBlockBlob(blobAddress, storageCredentials);
//retrieve blob contents
Console.WriteLine(blob.DownloadText());
Console.ReadLine();
}
static string GetMSIToken(string resourceID)
{
string accessToken = string.Empty;
// Build request to acquire MSI token
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + resourceID);
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
// Call /token endpoint
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Pipe response Stream to a StreamReader, and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
JavaScriptSerializer j = new JavaScriptSerializer();
Dictionary<string, string> list = (Dictionary<string, string>)j.Deserialize(stringResponse, typeof(Dictionary<string, string>));
accessToken = list["access_token"];
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
return accessToken;
}
}
}
尝试使用 C# 语言在 Azure 中使用系统分配的托管标识时出现 "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature." 错误。
遵循的步骤
- 创建了一个启用身份的新虚拟机(系统分配)
- 使用存储帐户中的角色分配在 IAM 中添加了 VM
- 能够使用 C# 生成令牌
- 但是在读取 blob 时出现异常,以下是异常详细信息
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
at Microsoft.Azure.Storage.Core.Executor.Executor.<ExecuteAsync>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.Storage.Blob.CloudBlockBlob.<DownloadTextAsync>d__72.MoveNext()
计划Class
class Program
{
static void Main(string[] args)
{
try
{
var blob = new AzureCloudBlob();
Console.WriteLine(blob.ReadBlob());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
AzureCloudBlob class,用于使用系统分配的托管标识访问令牌连接和读取 blob
class AzureCloudBlob
{
public CloudBlockBlob CreateConnection()
{
var token = GetToken();
var tokenCredentials = new TokenCredential(token);
var storageCredentials = new StorageCredentials(tokenCredentials);
var serviceUri = new Uri("https://mystorageacc.blob.core.windows.net/practice/blob.txt");
return new CloudBlockBlob(serviceUri, storageCredentials);
}
public string ReadBlob()
{
var client = CreateConnection();
var blobClient = client.DownloadTextAsync();
var data = blobClient.Result;
return data;
}
public string GetToken()
{
var request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/");
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var list = (Dictionary<string, string>)JsonConvert.DeserializeObject(stringResponse, typeof(Dictionary<string, string>));
string accessToken = list["access_token"];
Console.WriteLine(accessToken);
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
Console.WriteLine(errorText);
return errorText;
}
}
}
如果您想使用 Azure AD 身份验证访问 Azure 存储,我们应该使用 resouce=https://storage.azure.com/
来获取 Azure AD 访问令牌。但是你用resource=https://management.azure.com/
。请更换它。此外,请注意您需要为 MSI 分配正确的角色。角色应该是 Storage Blob Data Reader、Storage Blob Data Contributor 或 Storage Blob Data Owner.
详细步骤如下
在 Azure VM 上启用系统分配的托管标识
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
在存储帐户范围内将角色分配给 MSI
$sp =Get-AzADServicePrincipal -displayname "<your VM name>" New-AzRoleAssignment -ObjectId $sp.id ` -RoleDefinitionName "Storage Blob Data Reader" ` -Scope "/subscriptions/<subscription>/resourceGroups/sample-resource-group/providers/Microsoft.Storage/storageAccounts/<storage-account>"
代码
class Program { static void Main(string[] args) { //get token string accessToken = GetMSIToken("https://storage.azure.com/"); //create token credential TokenCredential tokenCredential = new TokenCredential(accessToken); //create storage credentials StorageCredentials storageCredentials = new StorageCredentials(tokenCredential); Uri blobAddress = new Uri("<URI to blob file>"); //create block blob using storage credentials CloudBlockBlob blob = new CloudBlockBlob(blobAddress, storageCredentials); //retrieve blob contents Console.WriteLine(blob.DownloadText()); Console.ReadLine(); } static string GetMSIToken(string resourceID) { string accessToken = string.Empty; // Build request to acquire MSI token HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + resourceID); request.Headers["Metadata"] = "true"; request.Method = "GET"; try { // Call /token endpoint HttpWebResponse response = (HttpWebResponse)request.GetResponse(); // Pipe response Stream to a StreamReader, and extract access token StreamReader streamResponse = new StreamReader(response.GetResponseStream()); string stringResponse = streamResponse.ReadToEnd(); JavaScriptSerializer j = new JavaScriptSerializer(); Dictionary<string, string> list = (Dictionary<string, string>)j.Deserialize(stringResponse, typeof(Dictionary<string, string>)); accessToken = list["access_token"]; return accessToken; } catch (Exception e) { string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed"); return accessToken; } } }