<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AdamWhite</title>
    <description>The latest articles on DEV Community by AdamWhite (@adamwhite).</description>
    <link>https://dev.to/adamwhite</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F456352%2Fca04ec53-41b5-4bb5-85fb-223ebfc8ab6f.png</url>
      <title>DEV Community: AdamWhite</title>
      <link>https://dev.to/adamwhite</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamwhite"/>
    <language>en</language>
    <item>
      <title>Generating Sas Tokens using Azure Managed Identity (User Delegation)</title>
      <dc:creator>AdamWhite</dc:creator>
      <pubDate>Thu, 16 Sep 2021 07:51:19 +0000</pubDate>
      <link>https://dev.to/adamwhite/generating-sas-tokens-using-azure-managed-identity-user-delegation-1480</link>
      <guid>https://dev.to/adamwhite/generating-sas-tokens-using-azure-managed-identity-user-delegation-1480</guid>
      <description>&lt;p&gt;It's possible with Azure Blob Storage to generate a &lt;a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview"&gt;Shared Access Signature (SAS)&lt;/a&gt; which you can allow any third party limited access to a blob. This access can be limited in time and actions such as Read, Write, or more to a specific file held within blob storage. You can also provide access to the entire blob container if you wish. &lt;/p&gt;

&lt;p&gt;There is a new way to generate in the new &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/overview/azure/storage"&gt;Blob Storage SDK&lt;/a&gt;, and the big thing here is the ability to generate those SAS tokens without a storage account key.&lt;/p&gt;

&lt;h2&gt;
  
  
  User Delegation SAS
&lt;/h2&gt;

&lt;p&gt;The typical way to generate a SAS token in code requires the storage account key. This assumes you have the storage account key, and there are scenarios where you just won't have access to that. This is the situation I found myself in. You'll find many answers online, but none of them lead you down the right path if you are as unlucky as I was. &lt;br&gt;
If you need to use &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview"&gt;"Managed Identity" &lt;/a&gt; to control access to your storage accounts in code, which is something I highly recommend wherever possible as this is a security best practice. &lt;br&gt;
In this scenario, you won't have a storage account key, so you'll need to find another way to generate the shared access signatures. &lt;/p&gt;

&lt;p&gt;To do that, we need to use an approach called &lt;a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-user-delegation-sas-create-dotnet"&gt;"user delegation" SAS &lt;/a&gt;. By using a user delegation SAS, we can sign the signature with the Azure Ad credentials instead of the storage account key.&lt;/p&gt;

&lt;p&gt;I'll show you below exactly what code you need to generate the user delegation SAS URI with the .Net storage SDK; I'll also cover a few gotchas that caught me out and how to test this within visual studio locally. &lt;/p&gt;
&lt;h2&gt;
  
  
  Generating a User Delegation SAS
&lt;/h2&gt;

&lt;p&gt;Connecting to Azure Storage using Azure Active Directory Credentials is made incredibly easy thanks to the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet"&gt;DefaultAzureCredential&lt;/a&gt;. This helper class tries a variety of different techniques to source the credentials required to access a storage account. &lt;/p&gt;

&lt;p&gt;Firstly it checks for environment variables. If these aren't present, it attempts to use a managed identity (this is what you want in production!). Should that fail, it has a range of fallback options that it will try, and these are great for local development. It can use the credentials you logged into visual studio, Visual Studio (VS) Code, Azure CLI with. So in a range of development environments, this will work. I'll show you below how to set the managed identity you want to use in Visual Studio, which can be the one you've logged in with or something else entirely.&lt;/p&gt;

&lt;p&gt;Here's how you would use the &lt;code&gt;DefaultAzureCredential&lt;/code&gt; to create a &lt;code&gt;BlobServiceClient&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;storageAccountName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ducksandbadgersstore"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;storageAccountUriString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;storageAccountName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.blob.core.windows.net"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobServiceClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BlobServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storageAccountUriString&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, you've successfully created a blob service client using managed identity. You can test this by uploading a file into the storage account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobContainerClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobServiceClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobContainerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duckcontainer"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobContainerClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duck.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExistsAsync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is my secret blob"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UploadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All being well, you should have a file in your blob storage container. Which proves that managed identity is working. &lt;/p&gt;

&lt;p&gt;Now let's generate a shared access signature. The first start is to create a &lt;a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-user-delegation-sas-create-dotnet"&gt;user delegation key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The duration of this SAS token can only be set to a &lt;strong&gt;maximum of 7 days&lt;/strong&gt; Otherwise, you'll get an error if you request a longer duration. You also get an error if you mess up the dates, I sent in the same start and end date of "Now" due to a config issue. &lt;br&gt;
When that happens, you'll see an error with an HTTP status code of 400 (bad request) with the error being &lt;code&gt;"The value for one of the XML nodes is not in the correct format,"&lt;/code&gt; which isn't the most helpful error. If you get this, check the values, make sure they make sense! Asking for a SAS token that immediately expires isn't sensible!&lt;/p&gt;

&lt;p&gt;To get the user delegation key is this simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userDelegationKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;blobServiceClient&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUserDelegationKeyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                               &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the user delegation key with the BlobSasBuilder and BlobUriBuilder helpers to generate the SAS token URI. You can access the file or provide a token allowing someone write access to upload a file to your container. &lt;br&gt;
In the example below, I'm asking for a SAS token that's valid for 7 days for a specific file. &lt;br&gt;
Do not that the SAS token doesn't need to have the same lifetime as the user delegation key, but it &lt;strong&gt;cannot be longer&lt;/strong&gt; If you attempt to create a SAS token URI with a lifespan that's longer than the lifespan of the user delegation key, you will get a 403 error response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sasBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BlobSasBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;BlobContainerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlobContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// duckcontainer from above&lt;/span&gt;
    &lt;span class="n"&gt;BlobName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// duck.text from above&lt;/span&gt;
    &lt;span class="n"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// b for blob, c for container&lt;/span&gt;
    &lt;span class="n"&gt;StartsOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpiresOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;sasBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlobSasPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
                        &lt;span class="n"&gt;BlobSasPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// read and write permissions&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobUriBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BlobUriBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Sas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sasBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToSasQueryParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userDelegationKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;blobServiceClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sasUri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobUriBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUri&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sasUri can be used to download the file until the SAS token expires or the user delegation key expires. Whichever happens first will invalidate this SAS token.&lt;/p&gt;

&lt;p&gt;To test the SAS token URI, here's a simple bit of code that will download the file's contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobContentsString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sasUri&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobContentsString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpRequestException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sas token failed - Unable to download: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Locally
&lt;/h2&gt;

&lt;p&gt;If you attempt to test this locally or use a service in Azure, you may find that this doesn't work. There's a reason for that. &lt;br&gt;
You need to give the identity accessing the storage account some RBAC permissions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage Account Contributor&lt;/li&gt;
&lt;li&gt;Storage Blob Data Contributor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may surprise you, as being the owner of the storage account isn't sufficient. &lt;/p&gt;

&lt;p&gt;You can test this in Azure CLI and even see if you can tighten the permissions by attempting the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ACCOUNT_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ducksandbadgersstore"&lt;/span&gt;
&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"duckcontainer"&lt;/span&gt;

&lt;span class="c"&gt;# use this to test if you have the correct permissions&lt;/span&gt;
az storage blob exists &lt;span class="nt"&gt;--account-name&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_NAME&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
                        &lt;span class="nt"&gt;--container-name&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
                        &lt;span class="nt"&gt;--name&lt;/span&gt; duck.txt &lt;span class="nt"&gt;--auth-mode&lt;/span&gt; login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you haven't assigned the right RBAC permissions within the storage account, the above will fail. Head on into Azure, go to your storage account, then Access Control (IAM), then role assignments to set the permissions. Take a look at &lt;a href="https://docs.microsoft.com/en-us/azure/app-service/scenario-secure-app-access-storage"&gt;this article&lt;/a&gt; to see the various ways to grant the relevant RBAC permissions.&lt;/p&gt;

&lt;p&gt;I personally do this within the CLI myself, and it's this simple. &lt;br&gt;
Firstly look you our Azure AD object ID using our email address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$EMAIL_ADDRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adam@redturtlesoftware.com'&lt;/span&gt;
&lt;span class="nv"&gt;$OBJECT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; az ad user list &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"[?mail=='&lt;/span&gt;&lt;span class="nv"&gt;$EMAIL_ADDRESS&lt;/span&gt;&lt;span class="s2"&gt;'].objectId"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need the id of the storage account to set the RBAC permissions on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$STORAGE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; az storage account show &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_NAME&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return a string that contains the subscriptionId, resource group, and the storage account name.&lt;br&gt;
For example:&lt;code&gt;/subscriptions/770476dd-69ac-465d-96cc-gh12bc676chk/resourceGroups/badger-rg/providers/Microsoft.Storage/storageAccounts/ducksandbadgersstore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can add the RBAC permissions &lt;code&gt;Storage Blob Data Contributor&lt;/code&gt; role scoped to this storage account container with this information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az role assignment create &lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s2"&gt;"Storage Blob Data Contributor"&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="nt"&gt;--assignee&lt;/span&gt; &lt;span class="nv"&gt;$OBJECT_ID&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="nt"&gt;--scope&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STORAGE_ID&lt;/span&gt;&lt;span class="s2"&gt;/blobServices/default/containers/&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this still doesn't work for you, there is a gotcha to get around when working within visual studio. &lt;br&gt;
The DefaultAzureCredential may not select the correct Azure AD tenant id. There are two ways to get around this. Firstly you can set it in code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;azureCredentialOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DefaultAzureCredentialOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;azureCredentialOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VisualStudioTenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"5546dcdg-6581-66f0-a200-da76560045433s"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azureCredentialOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Visual Studio's Managed Identity
&lt;/h2&gt;

&lt;p&gt;Secondly, you can go into Visual Studio, go to Tools -&amp;gt; Options. Then Azure Service Authentication. It's here where you can select an account. Or even add another account. I mentioned above about using an account that isn't specifically the one you are logged into Visual Studio with.&lt;br&gt;
This is the settings section you would use to set another managed identity to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Managed Identities can sound a bit scary to those who haven't used them, but actually, they are incredibly simple to use and end up making code much simpler and more secure. &lt;br&gt;
In the past, I've had to manage load balancing of storage queues and passing in storage account keys by using Azure Key Vault. It's just a complication you can get rid of. &lt;/p&gt;

&lt;p&gt;On top of that, Managed Identities are a much more secure way for you to access your cloud resources, giving a fine grained control as to what can be done to those resources, an account key gives complete access, RBAC gives you the ability to use the principle of least privilege. &lt;br&gt;
Sure, when it comes to generating SAS tokens, there is an additional hoop to jump through, but it's still less of a hassle than setting up KeyVault and passing the secret into the code. &lt;/p&gt;

&lt;p&gt;Hopefully, the steps above have shown you how to set the correct RBAC roles to your local user or managed identity. The C# example code is enough to help you generate that user delegation key, which is the key to SAS token generation with managed identity. &lt;/p&gt;

&lt;p&gt;You are limited to a lifetime of 7 days with this approach, but that's a good thing. The longer something is open, the more likely it will be attacked. It's best practice to only have your SAS tokens alive for the shortest amount of time necessary. &lt;/p&gt;

</description>
      <category>azure</category>
      <category>blobstorage</category>
      <category>managedidentity</category>
      <category>aad</category>
    </item>
    <item>
      <title>Increase Productivity with custom google chrome search engines</title>
      <dc:creator>AdamWhite</dc:creator>
      <pubDate>Thu, 15 Apr 2021 07:17:23 +0000</pubDate>
      <link>https://dev.to/adamwhite/increase-productivity-with-custom-google-chrome-search-engines-10fp</link>
      <guid>https://dev.to/adamwhite/increase-productivity-with-custom-google-chrome-search-engines-10fp</guid>
      <description>&lt;p&gt;As a programmer, I spend a lot of my time searching through web-based software such as JIRA, Bitbucket, and Github. &lt;/p&gt;

&lt;p&gt;On a day-to-day basis, I'll be looking up tickets by their reference numbers. Why? Why can't I just get these tickets off of the current sprint board? &lt;br&gt;
Well, I don't know about you, but I'll be in the middle of a ticket, and a tester, business analyst, or product owner will drop me a message asking about XYZ-999. &lt;/p&gt;

&lt;p&gt;What on earth was XYZ-999? I've managed to save a large amount of time by introducing custom search engines into google for simple tasks as quickly searching for a ticket reference. &lt;/p&gt;

&lt;p&gt;Or even just looking up active pull requests or trying to find some documentation on confluence.&lt;/p&gt;

&lt;p&gt;In this short article, I'll share how I go about doing this so that you can save yourself some time, sure it's 20-30 seconds here or there, but that adds up over a year. &lt;/p&gt;
&lt;h2&gt;
  
  
  Adding a custom search engine to Chrome
&lt;/h2&gt;

&lt;p&gt;Open Chrome and right-click the address bar, and click &lt;code&gt;Manage search engines...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click the add button. Under the section &lt;code&gt;other search engines&lt;/code&gt;, a small popup will show with 3 form fields to fill in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine:
Keyword:
URL with %s in place of query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fill this in with the info about your search engine. For example, confluence my be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine: Wiki
Keyword: wiki
URL: https://&amp;lt;company&amp;gt;.atlassian.net/wiki/search?text=%s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to use a custom search engine
&lt;/h2&gt;

&lt;p&gt;Once you've added a custom search engine, you can use it by clicking on the address bar and using the keyword, followed by a space, then the text you want to search for, then press enter.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wiki which badger danced the best duck?&amp;lt;enter&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples from my uses
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine: Jira
Keyword: jira
URL: https://&amp;lt;company&amp;gt;.atlassian.net/browse/ref-%s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine: BBPRs
Keyword: bbpr
URL: https://bitbucket.org/&amp;lt;company&amp;gt;/&amp;lt;repo&amp;gt;/pullrequests/%s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine: Gihub_ProjectPRs
Keyword: ghppr
https://github.com/&amp;lt;company&amp;gt;/&amp;lt;project&amp;gt;/pulls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Engine: Gihub_AllPRs
Keyword: ghapr
https://github.com/pulls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;This has been a very short post, but it is what it says on the tin, a very simple post showing you an easy tip to make searching GitHub, Jira, etc., much easier.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
