{"id":188,"date":"2024-03-17T11:41:59","date_gmt":"2024-03-17T11:41:59","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/03\/17\/generate-local-net-secrets-from-azure-deployments\/"},"modified":"2024-03-17T18:34:31","modified_gmt":"2024-03-17T18:34:31","slug":"generate-local-net-secrets-from-azure-deployments","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/03\/17\/generate-local-net-secrets-from-azure-deployments\/","title":{"rendered":"Generate Local .NET Secrets from Azure Deployments"},"content":{"rendered":"<p>Often sample projects starts with a few \u201cmagic strings\u201d, those variables contains URLs and key information related to a deployment or external resources that we will have to change to use the sample. As an example in .NET it could look like this: <\/p>\n<p>string openAIEndpoint = &#8220;https:\/\/&#8221;;<br \/>\nstring openAIDeploymentName = &#8220;my-ai-model&#8221;;<br \/>\nstring openAiKey = &#8220;123456abcd789EFGH&#8221;;<br \/>\n\/\/ &#8230;<\/p>\n<p>This post shows how you to automatically generate .NET secrets from the Azure deployment and how your .NET app can read them. Users who would like to try your sample won\u2019t have to edit anything anymore! They will only have to deploy using azd up, and then dotnet run to execute the app. Sound interesting? Here are to implement it. <\/p>\n<p>The code for the entire project can be found on GitHub, in the <a href=\"https:\/\/github.com\/FBoucher\/hikerai\">fboucher\/hikerai<\/a>.<\/p>\n<h2>The Preparation<\/h2>\n<p>Bicep is a language for creating infrastructure definitions for Azure resources that you want to deploy. Since these resources will have information like endpoints or model names, we want a way to export those, and in Bicep we use the output syntax:<\/p>\n<p>\/\/ some bicep deployment&#8230;<\/p>\n<p>output AZURE_OPENAI_ENDPOINT string = ai.outputs.AZURE_OPENAI_ENDPOINT<br \/>\noutput AZURE_OPENAI_GPT_NAME string = ai.outputs.AZURE_OPENAI_GPT_NAME<br \/>\noutput AZURE_OPENAI_KEY string = ai.outputs.AZURE_OPENAI_KEY<\/p>\n<p>With the Azure Developer CLI (azd) the command azd env get-values returns all those values in list of key-paired values. A PowerShell or Bash script could read those and create new .NET secrets using the command dotnet user-secrets set. Here the script postprovision.ps1.<\/p>\n<p>function Set-DotnetUserSecrets {<br \/>\n    param ($path, $lines)<br \/>\n    Push-Location<br \/>\n    cd $path<br \/>\n    dotnet user-secrets init<br \/>\n    dotnet user-secrets clear<br \/>\n    foreach ($line in $lines) {<br \/>\n        $name, $value = $line -split &#8216;=&#8217;<br \/>\n        $value = $value -replace &#8216;&#8221;&#8216;, &#8221;<br \/>\n        $name = $name -replace &#8216;__&#8217;, &#8216;:&#8217;<br \/>\n        if ($value -ne &#8221;) {<br \/>\n            dotnet user-secrets set $name $value | Out-Null<br \/>\n        }<br \/>\n    }<br \/>\n    Pop-Location<br \/>\n}<\/p>\n<p>$lines = (azd env get-values) -split &#8220;`n&#8221;<br \/>\nSet-DotnetUserSecrets -path &#8220;.&#8221; -lines $lines<\/p>\n<p>This script start by creating a function Set-DotnetUserSecrets that takes two parameters. The first parameter $path will be used so the script can change directory to that location. This is essential to make sure the secrets are created where needed. The second parameter $lines contains all the substring where key-paired values are saved (ex: VARIABLE_NAME=\u201dvariable_value\u201d). The script loops through all lines to isolate the names and the values and create a secret for each of them dotnet user-secrets set $name $value.<\/p>\n<p>On the two last lines, the script retrieves the environment variable generated by the outputs using azd env get-values and split the result in substring. It will finally call the function Set-DotnetUserSecrets declared previously passing the the path and lines. A postprovision.sh version of the script is also available in the repository.<\/p>\n<p>To execute the script after the deployment we need to add a post provision hook into the azure.yaml file. The azure.yaml is the schema that defines and describes the apps and types of Azure resources that are included in a project. Here how it looks.<\/p>\n<p>hooks:<br \/>\n  postprovision:<br \/>\n    windows:<br \/>\n      shell: pwsh<br \/>\n      run: .\/infra\/post-script\/postprovision.ps1<br \/>\n      interactive: true<br \/>\n      continueOnError: true<\/p>\n<h2>The deployment<\/h2>\n<p>Execute your deployment using Azure CLI Developer CLI command azd up. You can use the code of HikerAI available at <a href=\"https:\/\/github.com\/FBoucher\/hikerai\">fboucher\/hikerai<\/a>. Once you clone or download the repository, make sure you are at the root directory, to deploy the solution.<\/p>\n<p>To test that the secrets have been created used the command dotnet user-secrets list.<\/p>\n\n<h2>Use the Secrets<\/h2>\n<p>Using the NuGet package Microsoft.Extensions.Configuration, the application will be able to read the user secrets. You can now replace those magic strings.<\/p>\n<p>\/\/ == Retrieve the local secrets saved during the Azure deployment ==========<br \/>\nusing Microsoft.Extensions.Configuration;<\/p>\n<p>var config = new ConfigurationBuilder().AddUserSecrets&lt;Program&gt;().Build();<br \/>\nstring openAIEndpoint = config[&#8220;AZURE_OPENAI_ENDPOINT&#8221;];<br \/>\nstring openAIDeploymentName = config[&#8220;AZURE_OPENAI_GPT_NAME&#8221;];<br \/>\nstring openAiKey = config[&#8220;AZURE_OPENAI_KEY&#8221;];<\/p>\n<h2>Video<\/h2>\n\n<h2>Summary<\/h2>\n<p>This post shared a few tips to replace \u201cmagic strings\u201d by user secrets that can be automatically generates from the Azure deployment outputs. Try it in your solution or with our sample available ay <a href=\"https:\/\/github.com\/FBoucher\/hikerai\">fboucher\/hikerai<\/a> in the HikerAI folder.<\/p>\n<h3>References<\/h3>\n<p>All code from sample HikerAI <a href=\"https:\/\/github.com\/FBoucher\/hikerai\">fboucher\/hikerai<\/a><br \/>\n<a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-developer-cli\/overview\">Azure Developer CLI<\/a><br \/>\n<a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/security\/app-secrets?view=aspnetcore-8.0&amp;tabs=windows#secret-manager\">App secrets in development<\/a><\/p>\n<p>The post <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/generate-dotnet-secrets-automatically-from-azure-deployment\/\">Generate Local .NET Secrets from Azure Deployments<\/a> appeared first on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\">.NET Blog<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>Often sample projects starts with a few \u201cmagic strings\u201d, those variables contains URLs and key information related to a deployment [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":189,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[7],"tags":[],"class_list":["post-188","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=188"}],"version-history":[{"count":1,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/188\/revisions"}],"predecessor-version":[{"id":228,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/188\/revisions\/228"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/189"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}