[{"data":1,"prerenderedAt":1324},["Reactive",2],{"navigation":3,"/posts/http-clients-secrets":204,"/posts/http-clients-secrets-surround":1297},[4,192,200],{"title":5,"_path":6,"children":7,"icon":191},"Blog","/posts",[8,11,14,17,20,23,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,77,80,83,86,89,92,95,98,101,104,107,110,113,116,119,122,125,128,131,134,137,140,143,146,149,152,155,158,161,164,167,170,173,176,179,182,185,188],{"title":9,"_path":10},"Testing your API with REST Client","/posts/testing-your-api-with-rest-client",{"title":12,"_path":13},"HTML templating in Xamarin","/posts/html-templating-in-xamarin",{"title":15,"_path":16},"Goodbye Azure Portal, Welcome Azure CLI","/posts/welcome-azure-cli",{"title":18,"_path":19},"Coming across Gitpod","/posts/gitpod",{"title":21,"_path":22},"Handle token retrieval while querying an API","/posts/delegating-handler",{"title":24,"_path":25},"Clean up your local git branches.","/posts/cleaning-git-branches",{"title":27,"_path":28},"Automate configuration of Teams Tab SSO with PowerShell.","/posts/teams-sso-powershell",{"title":30,"_path":31},"How to do a technology watch? - Part 1","/posts/technology-watch-part1",{"title":33,"_path":34},"How to do a technology watch? - Part 2","/posts/technology-watch-part2",{"title":36,"_path":37},"You almost no longer need Key Vault references for Azure Functions.","/posts/azure-functions-custom-configuration",{"title":39,"_path":40},"How to do a technology watch? - Part 3","/posts/technology-watch-part3",{"title":42,"_path":43},"Forget DevOps, the future is already here!","/posts/devops-future",{"title":45,"_path":46},"Week 9, 2021 - Tips I learned this week","/posts/w09-2021-tips-learned-this-week",{"title":48,"_path":49},"Week 12, 2021 - Tips I learned this week","/posts/w12-2021-tips-learned-this-week",{"title":51,"_path":52},"Week 14, 2021 - Tips I learned this week","/posts/w14-2021-tips-learned-this-week",{"title":54,"_path":55},"Once upon a time in .NET","/posts/once-upon-a-time-in-dotnet",{"title":57,"_path":58},"Install your applications with winget","/posts/winget-import",{"title":60,"_path":61},"Customize your applications when installing them with winget","/posts/winget-override",{"title":63,"_path":64},"Week 22, 2021 - Tips I learned this week","/posts/w22-2021-tips-learned-this-week",{"title":66,"_path":67},"How to connect to an Azure SQL Database from C# using Azure AD","/posts/sqlclient-active-directory-authent",{"title":69,"_path":70},"Producing packages for Windows Package Manager","/posts/wingetcreate",{"title":72,"_path":73},"4 tips about GitHub Actions environment variables and contexts","/posts/github-actions-var-and-context",{"title":75,"_path":76},"AzureWebJobsStorage, the secret you don't need in your Function App.","/posts/azure-functions-without-azurewebjobsstorage",{"title":78,"_path":79},"ASP.NET Core - Lost in configuration","/posts/lost-in-configuration",{"title":81,"_path":82},"Week 39, 2021 - Tips I learned this week","/posts/w39-2021-tips-learned-this-week",{"title":84,"_path":85},"Week 41, 2021 - Tips I learned this week","/posts/w41-2021-tips-learned-this-week",{"title":87,"_path":88},"Migrating and open-sourcing my blog","/posts/migrating-blog",{"title":90,"_path":91},"Week 45, 2021 - Tips I learned this week","/posts/w45-2021-tips-learned-this-week",{"title":93,"_path":94},"Organize your GitHub stars with Astral","/posts/astral",{"title":96,"_path":97},"Pulumi with an Azure Blob Storage backend","/posts/pulumi-azure-backend",{"title":99,"_path":100},"IaC Hot Reload with Pulumi Watch","/posts/pulumi-watch",{"title":102,"_path":103},"Week 2, 2022 - Tips I learned this week","/posts/w02-2022-tips-learned-this-week",{"title":105,"_path":106},"Week 3, 2022 - Tips I learned this week","/posts/w03-2022-tips-learned-this-week",{"title":108,"_path":109},"Week 5, 2022 - Tips I learned this week","/posts/w05-2022-tips-learned-this-week",{"title":111,"_path":112},"How to provision an Azure SQL Database with Active Directory authentication","/posts/sqldatabase-active-directory-authent",{"title":114,"_path":115},"Why will I choose Pulumi over Terraform for my next project?","/posts/pulumi-vs-terraform",{"title":117,"_path":118},"Week 19, 2022 - Tips I learned this week","/posts/w19-2022-tips-learned-this-week",{"title":120,"_path":121},"Week 20, 2022 - Tips I learned this week","/posts/w20-2022-tips-learned-this-week",{"title":123,"_path":124},"Keeping secrets secure when using API Clients","/posts/http-clients-secrets",{"title":126,"_path":127},"What made me want to be a developer?","/posts/be-a-developer",{"title":129,"_path":130},"What can we do when stuck with a programming problem?","/posts/get-unstuck",{"title":132,"_path":133},"How did I automate the setup of my developer Windows laptop?","/posts/automate-developer-machine",{"title":135,"_path":136},"Discussion about API clients","/posts/http-clients",{"title":138,"_path":139},"Week 46, 2022 - Tips I learned this week","/posts/w46-2022-tips-learned-this-week",{"title":141,"_path":142},"When Pulumi met Nuke: a .NET love story","/posts/when-pulumi-met-nuke",{"title":144,"_path":145},"A year of learning and sharing - Dev Retro 2022","/posts/2022-retro",{"title":147,"_path":148},"Perform Dynamic Execution of an npm Package","/posts/pnpm-dlx",{"title":150,"_path":151},"Manage multiple Node.js versions","/posts/pnpm-env",{"title":153,"_path":154},"Introducing the Vue.js CI/CD series","/posts/vuecicd-introduction",{"title":156,"_path":157},"Execute commands using your project dependencies","/posts/pnpm-exec",{"title":159,"_path":160},"Vue.js CI/CD: Continuous Integration","/posts/vuecicd-ci",{"title":162,"_path":163},"Who is using pnpm?","/posts/pnpm-who-is-using",{"title":165,"_path":166},"Create an Azure-Ready GitHub Repository using Pulumi","/posts/azure-ready-github-repository",{"title":168,"_path":169},"Deploying to Azure from Azure DevOps without secrets","/posts/ado-workload-identity-federation",{"title":171,"_path":172},"Effortlessly Configure GitHub Repositories for Azure Deployment via OIDC","/posts/scripting-azure-ready-github-repository",{"title":174,"_path":175},"Playing with the .NET 8 Web API template","/posts/playing-with-dotnet8",{"title":177,"_path":178},"Another year of sharing and learning - Dev Retro 2023","/posts/2023-retro",{"title":180,"_path":181},"Week 4, 2024 - Tips I learned this week","/posts/w04-2024-tips-learned-this-week",{"title":183,"_path":184},"Using dependency injection with Azure .NET SDK","/posts/azure-sdk-di",{"title":186,"_path":187},"Having Fun With IT Event Calendars","/posts/it-event-calendars",{"title":189,"_path":190},"Call your Azure AD B2C protected API with authenticated HTTP requests from your JetBrains IDE","/posts/http-clients-oauth2","i-heroicons-newspaper",{"title":193,"_path":194,"children":195,"icon":199},"Goodies","/goodies",[196],{"title":197,"_path":198},"My Git Cheat Sheet","/goodies/gitcheatsheet","i-heroicons-gift-solid",{"title":201,"_path":202,"icon":203},"About","/about","i-heroicons-user-circle-solid",{"_path":124,"_dir":205,"_draft":206,"_partial":206,"_locale":207,"title":123,"description":208,"lead":209,"date":210,"image":211,"badge":213,"tags":215,"body":221,"_type":1292,"_id":1293,"_source":1294,"_file":1295,"_extension":1296},"posts",false,"","When using some API Clients (like REST Client or the HTTP Client of JetBrains' IDEs), environment variables are stored in JSON files that can contain secrets. To share these files within a team, developers tend to send them by email or by messaging applications, which is not very convenient nor secure 🔐. I thought it would be a good idea to store these secrets directly in an Azure Key Vault and automate the generation of a JSON file containing the secrets using Azure CLI and Nushell.","Playing with Azure CLI and Nushell to generate a secret environment file to send HTTP requests","2022-08-01T00:00:00.000Z",{"src":212},"/images/padlock_1.jpg",{"label":214},"Tooling",[216,217,218,219,220],"tooling","HTTP","nushell","Azure CLI","Azure Key Vault",{"type":222,"children":223,"toc":1286},"root",[224,269,276,313,366,417,429,434,457,463,468,477,482,488,509,522,531,543,728,733,742,747,756,769,778,794,806,815,835,844,857,866,895,900,909,922,930,935,944,956,965,977,982,1250,1256,1261,1275,1280],{"type":225,"tag":226,"props":227,"children":228},"element","p",{},[229,232,241,243,250,252,258,260,267],{"type":230,"value":231},"text","When using some API Clients (like ",{"type":225,"tag":233,"props":234,"children":238},"a",{"href":235,"rel":236},"https://marketplace.visualstudio.com/items?itemName=humao.rest-client",[237],"nofollow",[239],{"type":230,"value":240},"REST Client",{"type":230,"value":242}," or the ",{"type":225,"tag":233,"props":244,"children":247},{"href":245,"rel":246},"https://www.jetbrains.com/help/rider/Http_client_in__product__code_editor.html",[237],[248],{"type":230,"value":249},"HTTP Client of JetBrains' IDEs",{"type":230,"value":251},"), environment variables are stored in JSON files that can contain secrets. To share these files within a team, developers tend to send them by email or by messaging applications, which is not very convenient nor secure 🔐. I thought it would be a good idea to store these secrets directly in an Azure Key Vault and automate the generation of a JSON file containing the secrets using ",{"type":225,"tag":233,"props":253,"children":256},{"href":254,"rel":255},"https://docs.microsoft.com/en-us/cli/azure/",[237],[257],{"type":230,"value":219},{"type":230,"value":259}," and ",{"type":225,"tag":233,"props":261,"children":264},{"href":262,"rel":263},"https://www.nushell.sh/",[237],[265],{"type":230,"value":266},"Nushell",{"type":230,"value":268},".",{"type":225,"tag":270,"props":271,"children":273},"h2",{"id":272},"the-problem-keep-secrets-secure-while-making-http-requests",[274],{"type":230,"value":275},"The problem: keep secrets secure while making HTTP requests",{"type":225,"tag":226,"props":277,"children":278},{},[279,281,287,289,295,297,303,305,311],{"type":230,"value":280},"If you have read my article \"",{"type":225,"tag":233,"props":282,"children":285},{"href":283,"rel":284},"https://www.techwatching.dev/posts/testing-your-api-with-rest-client",[237],[286],{"type":230,"value":9},{"type":230,"value":288},"\", you know I am a big fan of using the vscode extension ",{"type":225,"tag":290,"props":291,"children":293},"code",{"className":292},[],[294],{"type":230,"value":240},{"type":230,"value":296}," to make HTTP requests instead of using GUI tools like Postman. With REST Client, you write your HTTP requests using the standard RFC 2616 in ",{"type":225,"tag":290,"props":298,"children":300},{"className":299},[],[301],{"type":230,"value":302},".http",{"type":230,"value":304}," or ",{"type":225,"tag":290,"props":306,"children":308},{"className":307},[],[309],{"type":230,"value":310},".rest",{"type":230,"value":312}," files and commit them to your git repository. You can define environments and their associated variables in the workspace settings file of vscode (you can also store them in the user settings file but I don't recommend it as they would apply to every vscode workspace). If you have some secrets among your environment variables (like an API key for instance), you obviously can't commit this settings file (you should never commit secrets to a git repository). So sharing among your developer team the environment variables needed to run the requests can be difficult.",{"type":225,"tag":314,"props":315,"children":319},"pre",{"className":316,"code":317,"language":318,"meta":207,"style":207},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","### Get Luke Skywalker\nGET https://swapi.co/api/people/?search=Luke HTTP/1.1\n","http",[320],{"type":225,"tag":290,"props":321,"children":322},{"__ignoreMap":207},[323,335],{"type":225,"tag":324,"props":325,"children":328},"span",{"class":326,"line":327},"line",1,[329],{"type":225,"tag":324,"props":330,"children":332},{"style":331},"--shiki-light:#90A4AE;--shiki-default:#546E7A;--shiki-dark:#676E95;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[333],{"type":230,"value":334},"### Get Luke Skywalker\n",{"type":225,"tag":324,"props":336,"children":338},{"class":326,"line":337},2,[339,345,351,356,361],{"type":225,"tag":324,"props":340,"children":342},{"style":341},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[343],{"type":230,"value":344},"GET",{"type":225,"tag":324,"props":346,"children":348},{"style":347},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[349],{"type":230,"value":350}," https://swapi.co/api/people/?search=Luke ",{"type":225,"tag":324,"props":352,"children":354},{"style":353},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[355],{"type":230,"value":217},{"type":225,"tag":324,"props":357,"children":358},{"style":347},[359],{"type":230,"value":360},"/",{"type":225,"tag":324,"props":362,"children":363},{"style":353},[364],{"type":230,"value":365},"1.1\n",{"type":225,"tag":226,"props":367,"children":368},{},[369,371,378,380,386,388,393,394,399,401,407,409,415],{"type":230,"value":370},"I have been using recently the IDE ",{"type":225,"tag":233,"props":372,"children":375},{"href":373,"rel":374},"https://www.jetbrains.com/fr-fr/rider/",[237],[376],{"type":230,"value":377},"Rider",{"type":230,"value":379},", which has (like all the other JetBrains' IDEs) an integrated ",{"type":225,"tag":233,"props":381,"children":383},{"href":245,"rel":382},[237],[384],{"type":230,"value":385},"HTTP Client",{"type":230,"value":387},". It's very similar to REST Client (same syntax for the requests that are written in ",{"type":225,"tag":290,"props":389,"children":391},{"className":390},[],[392],{"type":230,"value":302},{"type":230,"value":304},{"type":225,"tag":290,"props":395,"children":397},{"className":396},[],[398],{"type":230,"value":310},{"type":230,"value":400}," files) with some extra features. With this HTTP Client, environment variables are stored in a public JSON environment file ",{"type":225,"tag":290,"props":402,"children":404},{"className":403},[],[405],{"type":230,"value":406},"http-client.env.json",{"type":230,"value":408}," that can be committed. However, secrets can be stored in a private JSON environment file ",{"type":225,"tag":290,"props":410,"children":412},{"className":411},[],[413],{"type":230,"value":414},"http-client.private.env.json",{"type":230,"value":416}," that will not be committed and whose values will override the values in the public file. It's well thought out, yet we still have the problem of sharing with our team the private file containing the secrets.",{"type":225,"tag":226,"props":418,"children":419},{},[420],{"type":225,"tag":421,"props":422,"children":428},"img",{"alt":423,"className":424,"src":427},"HTTP file, HTTP environment file, and HTTP private environment opnened file in Rider.",[425,426],"rounded-lg","mx-auto","/posts/images/httpclientssecrets_rider_1.png",[],{"type":225,"tag":226,"props":430,"children":431},{},[432],{"type":230,"value":433},"When someone joins the team or new environment variables have been added, the developer in the team that has the latest version of the environment file usually share it by sending it by email or private message in Microsoft Teams, or Slack... to those who need it. This is not very convenient and this is not a good practice because you don't want secrets floating around. So what can we do about that?",{"type":225,"tag":435,"props":436,"children":438},"callout",{"icon":437},"i-heroicons-chat-bubble-left-20-solid",[439],{"type":225,"tag":226,"props":440,"children":441},{},[442,444,449,450,455],{"type":230,"value":443},"To be honest, even if sharing secrets like that bothered me a bit, I only decided to think of a solution when a friend pointed out to me that the big challenge with tools like ",{"type":225,"tag":290,"props":445,"children":447},{"className":446},[],[448],{"type":230,"value":240},{"type":230,"value":304},{"type":225,"tag":290,"props":451,"children":453},{"className":452},[],[454],{"type":230,"value":385},{"type":230,"value":456}," from JetBrains was managing secrets.",{"type":225,"tag":270,"props":458,"children":460},{"id":459},"the-solution-use-azure-key-vault-and-scripting",[461],{"type":230,"value":462},"The solution: use Azure Key Vault and scripting",{"type":225,"tag":226,"props":464,"children":465},{},[466],{"type":230,"value":467},"The solution is not complicated. I asked myself: where do I usually store secrets? The answer is \"a vault\". Whether it is Azure Key Vault, AWS Secret Manager, Google Cloud Secret Manager, or HashiCorp Vault it does not matter, secrets have to be stored somewhere safe, and it's precisely the purpose of a vault 🔒. I use Azure Key Vault when developing applications so that's what I am going to use as well for secrets needed for sending HTTP requests. If I want my team to be able to retrieve the secrets I just have to ensure everyone has access to the Key Vault.",{"type":225,"tag":435,"props":469,"children":471},{"icon":470},"i-heroicons-light-bulb",[472],{"type":225,"tag":226,"props":473,"children":474},{},[475],{"type":230,"value":476},"By the way, I like to create an Azure AD Group for my team so that all the permissions given in Azure (for the project the team is working on) are assigned to this group instead of to each developer. When someone joins or leaves the team, we then can simply add him to the group or remove him from it.",{"type":225,"tag":226,"props":478,"children":479},{},[480],{"type":230,"value":481},"If the secrets are stored in an Azure Key Vault, we can let each developer retrieve the secrets from the vault and put them in their private environment file. But honestly, it's not convenient, especially with many secrets. A better solution is to make a script that automatically retrieves the secrets and generates the JSON file. That way the git repository will contain the HTTP requests, the public environment file, and a script to generate the private environment file so that any new joiner will have everything he needs to get started and run the requests.",{"type":225,"tag":270,"props":483,"children":485},{"id":484},"lets-script-that-with-azure-cli-and-nushell",[486],{"type":230,"value":487},"Let's script that with Azure CLI and Nushell!",{"type":225,"tag":226,"props":489,"children":490},{},[491,493,499,501,507],{"type":230,"value":492},"I have chosen to script that using Azure CLI and Nushell because these are 2 tools I like and I am confident the resulting script will be concise and not too difficult to write. If you are not familiar with Azure CLI, you can check my article \"",{"type":225,"tag":233,"props":494,"children":497},{"href":495,"rel":496},"https://www.techwatching.dev/posts/welcome-azure-cli",[237],[498],{"type":230,"value":15},{"type":230,"value":500},"\". If you don't know Nushell you can check its ",{"type":225,"tag":233,"props":502,"children":504},{"href":262,"rel":503},[237],[505],{"type":230,"value":506},"website",{"type":230,"value":508}," or just continue reading this article to see how nice this shell is.",{"type":225,"tag":226,"props":510,"children":511},{},[512,514,520],{"type":230,"value":513},"I have already created an Azure Key Vault named ",{"type":225,"tag":290,"props":515,"children":517},{"className":516},[],[518],{"type":230,"value":519},"httpclient-vault",{"type":230,"value":521}," and set 3 secrets in it.",{"type":225,"tag":226,"props":523,"children":524},{},[525],{"type":225,"tag":421,"props":526,"children":530},{"alt":527,"className":528,"src":529},"The Secrets view of an Azure Key Vault resource in Azure Portal",[425,426],"/posts/images/httpclientssecrets_keyvault_1.png",[],{"type":225,"tag":226,"props":532,"children":533},{},[534,536,541],{"type":230,"value":535},"What I am trying to achieve is to produce the following file ",{"type":225,"tag":290,"props":537,"children":539},{"className":538},[],[540],{"type":230,"value":414},{"type":230,"value":542},":",{"type":225,"tag":314,"props":544,"children":548},{"className":545,"code":546,"language":547,"meta":207,"style":207},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"development\":\n  {\n    \"ApiKey\": \"12345678\",\n    \"Username\": \"admin\",\n    \"UserPassword\": \"Password\"\n  }\n}\n","json",[549],{"type":225,"tag":290,"props":550,"children":551},{"__ignoreMap":207},[552,561,585,594,637,675,710,719],{"type":225,"tag":324,"props":553,"children":554},{"class":326,"line":327},[555],{"type":225,"tag":324,"props":556,"children":558},{"style":557},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[559],{"type":230,"value":560},"{\n",{"type":225,"tag":324,"props":562,"children":563},{"class":326,"line":337},[564,569,575,580],{"type":225,"tag":324,"props":565,"children":566},{"style":557},[567],{"type":230,"value":568},"  \"",{"type":225,"tag":324,"props":570,"children":572},{"style":571},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[573],{"type":230,"value":574},"development",{"type":225,"tag":324,"props":576,"children":577},{"style":557},[578],{"type":230,"value":579},"\"",{"type":225,"tag":324,"props":581,"children":582},{"style":557},[583],{"type":230,"value":584},":\n",{"type":225,"tag":324,"props":586,"children":588},{"class":326,"line":587},3,[589],{"type":225,"tag":324,"props":590,"children":591},{"style":557},[592],{"type":230,"value":593},"  {\n",{"type":225,"tag":324,"props":595,"children":597},{"class":326,"line":596},4,[598,603,609,613,617,622,628,632],{"type":225,"tag":324,"props":599,"children":600},{"style":557},[601],{"type":230,"value":602},"    \"",{"type":225,"tag":324,"props":604,"children":606},{"style":605},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[607],{"type":230,"value":608},"ApiKey",{"type":225,"tag":324,"props":610,"children":611},{"style":557},[612],{"type":230,"value":579},{"type":225,"tag":324,"props":614,"children":615},{"style":557},[616],{"type":230,"value":542},{"type":225,"tag":324,"props":618,"children":619},{"style":557},[620],{"type":230,"value":621}," \"",{"type":225,"tag":324,"props":623,"children":625},{"style":624},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[626],{"type":230,"value":627},"12345678",{"type":225,"tag":324,"props":629,"children":630},{"style":557},[631],{"type":230,"value":579},{"type":225,"tag":324,"props":633,"children":634},{"style":557},[635],{"type":230,"value":636},",\n",{"type":225,"tag":324,"props":638,"children":640},{"class":326,"line":639},5,[641,645,650,654,658,662,667,671],{"type":225,"tag":324,"props":642,"children":643},{"style":557},[644],{"type":230,"value":602},{"type":225,"tag":324,"props":646,"children":647},{"style":605},[648],{"type":230,"value":649},"Username",{"type":225,"tag":324,"props":651,"children":652},{"style":557},[653],{"type":230,"value":579},{"type":225,"tag":324,"props":655,"children":656},{"style":557},[657],{"type":230,"value":542},{"type":225,"tag":324,"props":659,"children":660},{"style":557},[661],{"type":230,"value":621},{"type":225,"tag":324,"props":663,"children":664},{"style":624},[665],{"type":230,"value":666},"admin",{"type":225,"tag":324,"props":668,"children":669},{"style":557},[670],{"type":230,"value":579},{"type":225,"tag":324,"props":672,"children":673},{"style":557},[674],{"type":230,"value":636},{"type":225,"tag":324,"props":676,"children":678},{"class":326,"line":677},6,[679,683,688,692,696,700,705],{"type":225,"tag":324,"props":680,"children":681},{"style":557},[682],{"type":230,"value":602},{"type":225,"tag":324,"props":684,"children":685},{"style":605},[686],{"type":230,"value":687},"UserPassword",{"type":225,"tag":324,"props":689,"children":690},{"style":557},[691],{"type":230,"value":579},{"type":225,"tag":324,"props":693,"children":694},{"style":557},[695],{"type":230,"value":542},{"type":225,"tag":324,"props":697,"children":698},{"style":557},[699],{"type":230,"value":621},{"type":225,"tag":324,"props":701,"children":702},{"style":624},[703],{"type":230,"value":704},"Password",{"type":225,"tag":324,"props":706,"children":707},{"style":557},[708],{"type":230,"value":709},"\"\n",{"type":225,"tag":324,"props":711,"children":713},{"class":326,"line":712},7,[714],{"type":225,"tag":324,"props":715,"children":716},{"style":557},[717],{"type":230,"value":718},"  }\n",{"type":225,"tag":324,"props":720,"children":722},{"class":326,"line":721},8,[723],{"type":225,"tag":324,"props":724,"children":725},{"style":557},[726],{"type":230,"value":727},"}\n",{"type":225,"tag":226,"props":729,"children":730},{},[731],{"type":230,"value":732},"First, let's list the secrets in the Key Vault:",{"type":225,"tag":226,"props":734,"children":735},{},[736],{"type":225,"tag":421,"props":737,"children":741},{"alt":738,"className":739,"src":740},"An Azure CLI command that lists Key Vault secrets in terminal.",[425,426],"/posts/images/httpclientssecrets_script_1.png",[],{"type":225,"tag":226,"props":743,"children":744},{},[745],{"type":230,"value":746},"The output of the command is not that easy to read because it's JSON and there are some properties we are not interested in. However, Azure CLI supports different output formats and can be used with JMESPath expressions to query the output of a command like this:",{"type":225,"tag":226,"props":748,"children":749},{},[750],{"type":225,"tag":421,"props":751,"children":755},{"alt":752,"className":753,"src":754},"An Azure CLI command using JMESPath that lists Key Vault secrets in terminal.",[425,426],"/posts/images/httpclientssecrets_script_2.png",[],{"type":225,"tag":226,"props":757,"children":758},{},[759,761,767],{"type":230,"value":760},"It's nice but I won't need to use this because I can use Nushell (aka Nu) pipelines where everything is structured data that can be filtered, selected, and sorted. To bring the Azure CLI command output into a Nu pipeline, I can use the ",{"type":225,"tag":290,"props":762,"children":764},{"className":763},[],[765],{"type":230,"value":766},"from json",{"type":230,"value":768}," command.",{"type":225,"tag":226,"props":770,"children":771},{},[772],{"type":225,"tag":421,"props":773,"children":777},{"alt":774,"className":775,"src":776},"The output of the \"az keyvault secret list --vault-name httpclient-vault | from json\" command in terminal.",[425,426],"/posts/images/httpclientssecrets_script_3.png",[],{"type":225,"tag":435,"props":779,"children":780},{"icon":470},[781],{"type":225,"tag":226,"props":782,"children":783},{},[784,786,792],{"type":230,"value":785},"Nu has many ",{"type":225,"tag":290,"props":787,"children":789},{"className":788},[],[790],{"type":230,"value":791},"from",{"type":230,"value":793}," commands to convert data from different formats to structured data/table.",{"type":225,"tag":226,"props":795,"children":796},{},[797,799,805],{"type":230,"value":798},"You probably have noticed that the Azure CLI command we used to list the secrets does not provide their values. To retrieve the secret values we have to call another command for each secret using the id of the secret like this: ",{"type":225,"tag":290,"props":800,"children":802},{"className":801},[],[803],{"type":230,"value":804},"az keyvault secret show --id $secretId",{"type":230,"value":268},{"type":225,"tag":226,"props":807,"children":808},{},[809],{"type":225,"tag":421,"props":810,"children":814},{"alt":811,"className":812,"src":813},"An Azure CLI command that get a secret from Key Vault in terminal.",[425,426],"/posts/images/httpclientssecrets_script_4.png",[],{"type":225,"tag":226,"props":816,"children":817},{},[818,820,825,827,833],{"type":230,"value":819},"Again we can use the ",{"type":225,"tag":290,"props":821,"children":823},{"className":822},[],[824],{"type":230,"value":766},{"type":230,"value":826}," command, and the ",{"type":225,"tag":290,"props":828,"children":830},{"className":829},[],[831],{"type":230,"value":832},"get",{"type":230,"value":834}," command to only retrieve the value of a secret.",{"type":225,"tag":226,"props":836,"children":837},{},[838],{"type":225,"tag":421,"props":839,"children":843},{"alt":840,"className":841,"src":842},"The output of the nushell script retrieving a secret value from keyvault.",[425,426],"/posts/images/httpclientssecrets_script_5.png",[],{"type":225,"tag":226,"props":845,"children":846},{},[847,849,855],{"type":230,"value":848},"Now that we know how to retrieve the value of a secret, we can insert a new column ",{"type":225,"tag":290,"props":850,"children":852},{"className":851},[],[853],{"type":230,"value":854},"value",{"type":230,"value":856}," into our table that will be filled with the value of each secret retrieved   using the previous command:",{"type":225,"tag":226,"props":858,"children":859},{},[860],{"type":225,"tag":421,"props":861,"children":865},{"alt":862,"className":863,"src":864},"The output of the nushell script retrieving a list of secrets from keyvault.",[425,426],"/posts/images/httpclientssecrets_script_6.png",[],{"type":225,"tag":226,"props":867,"children":868},{},[869,871,877,879,885,887,893],{"type":230,"value":870},"The ",{"type":225,"tag":290,"props":872,"children":874},{"className":873},[],[875],{"type":230,"value":876},"{|secret| (az keyvault secret show --id $secret.id | from json | get value)}",{"type":230,"value":878}," part is a block that is executed for each row. The ",{"type":225,"tag":290,"props":880,"children":882},{"className":881},[],[883],{"type":230,"value":884},"secret",{"type":230,"value":886}," is the parameter of the block which represents the row, with the values of the columns for this row being available as properties of the variable ",{"type":225,"tag":290,"props":888,"children":890},{"className":889},[],[891],{"type":230,"value":892},"$secret",{"type":230,"value":894},". As the command was becoming long for a single line, we wrapped it in parentheses that allow us to write the command on multiple lines.",{"type":225,"tag":226,"props":896,"children":897},{},[898],{"type":230,"value":899},"As we are only interested in the columns \"name\" and \"value\", we only select them.",{"type":225,"tag":226,"props":901,"children":902},{},[903],{"type":225,"tag":421,"props":904,"children":908},{"alt":905,"className":906,"src":907},"The output of the nushell script retrieving Azure Key Vault secrets (name and value).",[425,426],"/posts/images/httpclientssecrets_script_7.png",[],{"type":225,"tag":226,"props":910,"children":911},{},[912,914,920],{"type":230,"value":913},"We have to reorganize the data to make key-value pairs where keys come from the column name and values from the column value. We can use the ",{"type":225,"tag":290,"props":915,"children":917},{"className":916},[],[918],{"type":230,"value":919},"transpose",{"type":230,"value":921}," with the proper flags to do that:",{"type":225,"tag":226,"props":923,"children":924},{},[925],{"type":225,"tag":421,"props":926,"children":929},{"alt":905,"className":927,"src":928},[425,426],"/posts/images/httpclientssecrets_script_8.png",[],{"type":225,"tag":226,"props":931,"children":932},{},[933],{"type":230,"value":934},"Then we wrap the key-value pairs in a JSON object corresponding to the development environment:",{"type":225,"tag":226,"props":936,"children":937},{},[938],{"type":225,"tag":421,"props":939,"children":943},{"alt":940,"className":941,"src":942},"The output of the nushell script creating a JSON object from Azure Key Vault secrets.",[425,426],"/posts/images/httpclientssecrets_script_9.png",[],{"type":225,"tag":226,"props":945,"children":946},{},[947,949,955],{"type":230,"value":948},"We can check we get the JSON we want with the ",{"type":225,"tag":290,"props":950,"children":952},{"className":951},[],[953],{"type":230,"value":954},"to json",{"type":230,"value":768},{"type":225,"tag":226,"props":957,"children":958},{},[959],{"type":225,"tag":421,"props":960,"children":964},{"alt":961,"className":962,"src":963},"The output of the nushell script creating a JSON string from Azure Key Vault secrets.",[425,426],"/posts/images/httpclientssecrets_script_10.png",[],{"type":225,"tag":226,"props":966,"children":967},{},[968,970,975],{"type":230,"value":969},"And finally, we can save the data in a ",{"type":225,"tag":290,"props":971,"children":973},{"className":972},[],[974],{"type":230,"value":414},{"type":230,"value":976}," file using the save command.",{"type":225,"tag":226,"props":978,"children":979},{},[980],{"type":230,"value":981},"Here is the final script 🔽:",{"type":225,"tag":314,"props":983,"children":986},{"className":984,"code":985,"language":218,"meta":207,"style":207},"language-nushell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","(\n  az keyvault secret list --vault-name httpclient-vault |\n  from json |\n  insert value {|secret| (az keyvault secret show --id $secret.id | from json | get value)} |\n  select name value |\n  transpose -rd |\n  { development: $in } |\n  save http-client.private.env.json\n)\n",[987],{"type":225,"tag":290,"props":988,"children":989},{"__ignoreMap":207},[990,998,1042,1054,1155,1176,1198,1228,1241],{"type":225,"tag":324,"props":991,"children":992},{"class":326,"line":327},[993],{"type":225,"tag":324,"props":994,"children":995},{"style":347},[996],{"type":230,"value":997},"(\n",{"type":225,"tag":324,"props":999,"children":1000},{"class":326,"line":337},[1001,1006,1011,1016,1021,1026,1032,1037],{"type":225,"tag":324,"props":1002,"children":1003},{"style":605},[1004],{"type":230,"value":1005},"  az",{"type":225,"tag":324,"props":1007,"children":1008},{"style":624},[1009],{"type":230,"value":1010}," keyvault",{"type":225,"tag":324,"props":1012,"children":1013},{"style":624},[1014],{"type":230,"value":1015}," secret",{"type":225,"tag":324,"props":1017,"children":1018},{"style":624},[1019],{"type":230,"value":1020}," list",{"type":225,"tag":324,"props":1022,"children":1023},{"style":341},[1024],{"type":230,"value":1025}," --",{"type":225,"tag":324,"props":1027,"children":1029},{"style":1028},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[1030],{"type":230,"value":1031},"vault-name",{"type":225,"tag":324,"props":1033,"children":1034},{"style":624},[1035],{"type":230,"value":1036}," httpclient-vault",{"type":225,"tag":324,"props":1038,"children":1039},{"style":341},[1040],{"type":230,"value":1041}," |\n",{"type":225,"tag":324,"props":1043,"children":1044},{"class":326,"line":587},[1045,1050],{"type":225,"tag":324,"props":1046,"children":1047},{"style":353},[1048],{"type":230,"value":1049},"  from json",{"type":225,"tag":324,"props":1051,"children":1052},{"style":341},[1053],{"type":230,"value":1041},{"type":225,"tag":324,"props":1055,"children":1056},{"class":326,"line":596},[1057,1062,1067,1072,1077,1081,1086,1091,1095,1099,1104,1108,1113,1118,1122,1127,1132,1137,1141,1146,1151],{"type":225,"tag":324,"props":1058,"children":1059},{"style":353},[1060],{"type":230,"value":1061},"  insert",{"type":225,"tag":324,"props":1063,"children":1064},{"style":624},[1065],{"type":230,"value":1066}," value",{"type":225,"tag":324,"props":1068,"children":1069},{"style":557},[1070],{"type":230,"value":1071}," {",{"type":225,"tag":324,"props":1073,"children":1074},{"style":347},[1075],{"type":230,"value":1076},"|",{"type":225,"tag":324,"props":1078,"children":1079},{"style":1028},[1080],{"type":230,"value":884},{"type":225,"tag":324,"props":1082,"children":1083},{"style":347},[1084],{"type":230,"value":1085},"| (",{"type":225,"tag":324,"props":1087,"children":1088},{"style":605},[1089],{"type":230,"value":1090},"az",{"type":225,"tag":324,"props":1092,"children":1093},{"style":624},[1094],{"type":230,"value":1010},{"type":225,"tag":324,"props":1096,"children":1097},{"style":624},[1098],{"type":230,"value":1015},{"type":225,"tag":324,"props":1100,"children":1101},{"style":624},[1102],{"type":230,"value":1103}," show",{"type":225,"tag":324,"props":1105,"children":1106},{"style":341},[1107],{"type":230,"value":1025},{"type":225,"tag":324,"props":1109,"children":1110},{"style":1028},[1111],{"type":230,"value":1112},"id",{"type":225,"tag":324,"props":1114,"children":1115},{"style":347},[1116],{"type":230,"value":1117}," $secret.id ",{"type":225,"tag":324,"props":1119,"children":1120},{"style":341},[1121],{"type":230,"value":1076},{"type":225,"tag":324,"props":1123,"children":1124},{"style":353},[1125],{"type":230,"value":1126}," from json",{"type":225,"tag":324,"props":1128,"children":1129},{"style":341},[1130],{"type":230,"value":1131}," |",{"type":225,"tag":324,"props":1133,"children":1134},{"style":353},[1135],{"type":230,"value":1136}," get",{"type":225,"tag":324,"props":1138,"children":1139},{"style":624},[1140],{"type":230,"value":1066},{"type":225,"tag":324,"props":1142,"children":1143},{"style":347},[1144],{"type":230,"value":1145},")",{"type":225,"tag":324,"props":1147,"children":1148},{"style":557},[1149],{"type":230,"value":1150},"}",{"type":225,"tag":324,"props":1152,"children":1153},{"style":341},[1154],{"type":230,"value":1041},{"type":225,"tag":324,"props":1156,"children":1157},{"class":326,"line":639},[1158,1163,1168,1172],{"type":225,"tag":324,"props":1159,"children":1160},{"style":353},[1161],{"type":230,"value":1162},"  select",{"type":225,"tag":324,"props":1164,"children":1165},{"style":624},[1166],{"type":230,"value":1167}," name",{"type":225,"tag":324,"props":1169,"children":1170},{"style":624},[1171],{"type":230,"value":1066},{"type":225,"tag":324,"props":1173,"children":1174},{"style":341},[1175],{"type":230,"value":1041},{"type":225,"tag":324,"props":1177,"children":1178},{"class":326,"line":677},[1179,1184,1189,1194],{"type":225,"tag":324,"props":1180,"children":1181},{"style":353},[1182],{"type":230,"value":1183},"  transpose",{"type":225,"tag":324,"props":1185,"children":1186},{"style":341},[1187],{"type":230,"value":1188}," -",{"type":225,"tag":324,"props":1190,"children":1191},{"style":1028},[1192],{"type":230,"value":1193},"rd",{"type":225,"tag":324,"props":1195,"children":1196},{"style":341},[1197],{"type":230,"value":1041},{"type":225,"tag":324,"props":1199,"children":1200},{"class":326,"line":712},[1201,1206,1211,1215,1220,1224],{"type":225,"tag":324,"props":1202,"children":1203},{"style":557},[1204],{"type":230,"value":1205},"  {",{"type":225,"tag":324,"props":1207,"children":1208},{"style":347},[1209],{"type":230,"value":1210}," development",{"type":225,"tag":324,"props":1212,"children":1213},{"style":341},[1214],{"type":230,"value":542},{"type":225,"tag":324,"props":1216,"children":1217},{"style":347},[1218],{"type":230,"value":1219}," $in ",{"type":225,"tag":324,"props":1221,"children":1222},{"style":557},[1223],{"type":230,"value":1150},{"type":225,"tag":324,"props":1225,"children":1226},{"style":341},[1227],{"type":230,"value":1041},{"type":225,"tag":324,"props":1229,"children":1230},{"class":326,"line":721},[1231,1236],{"type":225,"tag":324,"props":1232,"children":1233},{"style":353},[1234],{"type":230,"value":1235},"  save",{"type":225,"tag":324,"props":1237,"children":1238},{"style":624},[1239],{"type":230,"value":1240}," http-client.private.env.json\n",{"type":225,"tag":324,"props":1242,"children":1244},{"class":326,"line":1243},9,[1245],{"type":225,"tag":324,"props":1246,"children":1247},{"style":347},[1248],{"type":230,"value":1249},")\n",{"type":225,"tag":270,"props":1251,"children":1253},{"id":1252},"final-thoughts",[1254],{"type":230,"value":1255},"Final thoughts",{"type":225,"tag":226,"props":1257,"children":1258},{},[1259],{"type":230,"value":1260},"In this example, I scripted with Nu the retrieval of secrets from an Azure Key Vault, but it should not be too difficult to apply the same concepts to fetch secrets from another vault.",{"type":225,"tag":226,"props":1262,"children":1263},{},[1264,1266,1273],{"type":230,"value":1265},"I had fun playing with Azure CLI and Nushell to write this script but there are many other ways to do the same thing. There are also probably other tools or services (I have just came across ",{"type":225,"tag":233,"props":1267,"children":1270},{"href":1268,"rel":1269},"https://www.doppler.com/",[237],[1271],{"type":230,"value":1272},"Doppler",{"type":230,"value":1274}," which seems nice) that can help you manage secrets securely.",{"type":225,"tag":226,"props":1276,"children":1277},{},[1278],{"type":230,"value":1279},"I am not a Nushell expert but I find it awesome, and am considering making it my main shell. You should give it a try too. A big thank you to the people in the Nushell Discord that help me with my script ❤️.",{"type":225,"tag":1281,"props":1282,"children":1283},"style",{},[1284],{"type":230,"value":1285},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":207,"searchDepth":337,"depth":337,"links":1287},[1288,1289,1290,1291],{"id":272,"depth":337,"text":275},{"id":459,"depth":337,"text":462},{"id":484,"depth":337,"text":487},{"id":1252,"depth":337,"text":1255},"markdown","content:1.posts:39.http-clients-secrets.md","content","1.posts/39.http-clients-secrets.md","md",[1298,1310],{"_path":127,"_dir":205,"_draft":206,"_partial":206,"_locale":207,"title":126,"description":1299,"lead":1300,"date":1301,"badge":1302,"image":1304,"tags":1306,"_type":1292,"_id":1308,"_source":1294,"_file":1309,"_extension":1296},"In this article, I share some thoughts about what made me want to be and continue to be a developer.","Some thoughts about how and why I became a developer","2022-08-20T00:00:00.000Z",{"label":1303},"Essay",{"src":1305},"/images/everyone_can_code.jpg",[1307],"thoughts","content:1.posts:40.be-a-developer.md","1.posts/40.be-a-developer.md",{"_path":121,"_dir":205,"_draft":206,"_partial":206,"_locale":207,"title":120,"description":1311,"lead":1311,"date":1312,"image":1313,"badge":1315,"tags":1317,"_type":1292,"_id":1322,"_source":1294,"_file":1323,"_extension":1296},"The \"this\" in TypeScript, a must-have tool for Durable Functions, and a new git alias.","2022-05-23T00:00:00.000Z",{"src":1314},"/images/surface_1.jpg",{"label":1316},"Tips",[1318,1319,1320,1321,216],"tips learned this week","TypeScript","git","Azure Functions","content:1.posts:38.w20-2022-tips-learned-this-week.md","1.posts/38.w20-2022-tips-learned-this-week.md",1716749601560]