[{"data":1,"prerenderedAt":1275},["Reactive",2],{"navigation":3,"aAII9Cz3yR":204,"tags-Configuration":397},[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",[205,207,209,211,214,217,220,223,226,229,231,234,237,240,242,244,247,250,253,255,258,261,264,267,270,273,276,279,282,285,287,289,292,294,297,300,303,305,308,310,313,316,319,322,325,327,329,332,335,338,341,344,347,350,353,356,359,361,363,366,369,372,375,377,380,383,385,388,391,394],[206,206],"tooling",[208,208],"vscode",[210,210],"rest",[212,213],"http","HTTP",[215,216],"razor","Razor",[218,219],"xamarin","Xamarin",[221,222],"templating","Templating",[224,225],"azure-cli","Azure CLI",[227,228],"azure","Azure",[230,230],"shell",[232,233],"github","GitHub",[235,236],"asp-net-core","ASP.NET Core",[238,239],"net",".NET",[241,241],"git",[243,243],"nushell",[245,246],"microsoft-teams","Microsoft Teams",[248,249],"powershell","PowerShell",[251,252],"azure-active-directory","Azure Active Directory",[254,254],"learning",[256,257],"azure-functions","Azure Functions",[259,260],"azure-key-vault","Azure Key Vault",[262,263],"configuration","Configuration",[265,266],"devops","DevOps",[268,269],"it","IT",[271,272],"tips-learned-this-week","tips learned this week",[274,275],"windows-terminal","Windows Terminal",[277,278],"azure-pipelines","Azure Pipelines",[280,281],"application-insights","Application Insights",[283,284],"azure-iot","Azure IoT",[286,286],"records",[288,288],"refit",[290,291],"development-box-setup","development box setup",[293,293],"winget",[295,296],"package-manager","package manager",[298,299],"azure-sql-database","Azure SQL Database",[301,302],"azure-sdk","Azure SDK",[304,304],"wingetcreate",[306,307],"github-actions","GitHub Actions",[309,309],"jq",[311,312],"pulumi","Pulumi",[314,315],"iac","IaC",[317,318],"azure-storage","Azure Storage",[320,321],"azure-signalr","Azure SignalR",[323,324],"visio","Visio",[326,326],"csharp",[328,328],"jest",[330,331],"statiq","Statiq",[333,334],"open-source","open source",[336,337],"visual-studio","Visual Studio",[339,340],"vue-js","Vue.js",[342,343],"azure-devops","Azure DevOps",[345,346],"vite","Vite",[348,349],"code-analysis","Code analysis",[351,352],"diagram","Diagram",[354,355],"terraform","Terraform",[357,358],"typescript","TypeScript",[360,360],"thoughts",[362,362],"pnpm",[364,365],"nuke","Nuke",[367,368],"pipelines","Pipelines",[370,371],"cicd","CI/CD",[373,374],"openid-connect","OpenID Connect",[376,376],"security",[378,379],"github-cli","GitHub CLI",[381,382],"microsoft-entra-id","Microsoft Entra ID",[384,384],"advent",[386,387],"finops","FinOps",[389,390],"anglesharp","AngleSharp",[392,393],"oauth2","OAuth2",[395,396],"azure-ad-b2c","Azure AD B2C",[398,873],{"_path":106,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":105,"description":402,"lead":403,"date":404,"image":405,"badge":407,"tags":409,"body":410,"_type":868,"_id":869,"_source":870,"_file":871,"_extension":872},"posts",false,"",".NET configuration providers, Vite vscode extension, Azure DevOps pull request templates, and degit.","Tooling around .NET, Azure DevOps, and VS Code.","2022-01-24T00:00:00.000Z",{"src":406},"/images/surface_1.jpg",{"label":408},"Tips",[272,239,263,343,208,346,241,206],{"type":411,"children":412,"toc":861},"root",[413,422,478,490,503,612,634,640,663,668,677,683,688,702,715,728,733,742,747,752,757,766,783,789,794,829,838,850,855],{"type":414,"tag":415,"props":416,"children":418},"element","h2",{"id":417},"net-tip-of-the-week-the-new-way-to-add-a-configuration-source",[419],{"type":420,"value":421},"text",".NET tip of the week: the new way to add a configuration source",{"type":414,"tag":423,"props":424,"children":425},"p",{},[426,428,435,437,443,445,451,453,458,460,465,467,476],{"type":420,"value":427},".NET 6 introduced a new way to build a .NET application using the new ",{"type":414,"tag":429,"props":430,"children":432},"code",{"className":431},[],[433],{"type":420,"value":434},"WebApplication",{"type":420,"value":436}," and ",{"type":414,"tag":429,"props":438,"children":440},{"className":439},[],[441],{"type":420,"value":442},"WebApplicationBuilder",{"type":420,"value":444}," classes. One thing I like about it is how configuration is handled. Instead of using the ",{"type":414,"tag":429,"props":446,"children":448},{"className":447},[],[449],{"type":420,"value":450},"ConfigureAppConfiguration",{"type":420,"value":452}," method to add a new configuration source, you can directly use the ",{"type":414,"tag":429,"props":454,"children":456},{"className":455},[],[457],{"type":420,"value":263},{"type":420,"value":459}," property on the ",{"type":414,"tag":429,"props":461,"children":463},{"className":462},[],[464],{"type":420,"value":442},{"type":420,"value":466}," instance. You can see an example of this change on this screenshot of the ",{"type":414,"tag":468,"props":469,"children":473},"a",{"href":470,"rel":471},"https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-6.0#add-configuration-providers",[472],"nofollow",[474],{"type":420,"value":475},"ASP.NET Core documentation",{"type":420,"value":477},":",{"type":414,"tag":423,"props":479,"children":480},{},[481],{"type":414,"tag":482,"props":483,"children":489},"img",{"alt":484,"className":485,"src":488},"Using configuration provider in ASP.NET 5 versus ASP.NET 6.",[486,487],"rounded-lg","mx-auto","/posts/images/w032022tips_net_configuration_1.png",[],{"type":414,"tag":423,"props":491,"children":492},{},[493,495,501],{"type":420,"value":494},"You may think this way of adding a new configuration source does not bring much apart from making the code shorter. I thought it too until I discovered that it enables you to access configuration in the builder from previously registered configuration sources. For instance, if you want to load secrets from an Azure Key Vault into your configuration, you will need to retrieve the Key Vault URI from your configuration (that you may have set in your ",{"type":414,"tag":429,"props":496,"children":498},{"className":497},[],[499],{"type":420,"value":500},"appsettings.Development.json",{"type":420,"value":502}," file). Before you would have to partially build your configuration to get the value of a setting, now you can just access it.",{"type":414,"tag":504,"props":505,"children":508},"pre",{"className":506,"code":507,"language":326,"meta":401,"style":401},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","builder.Configuration.AddAzureKeyVault(new Uri(builder.Configuration[\"KeyVault:VaultUri\"]), new DefaultAzureCredential());\n",[509],{"type":414,"tag":429,"props":510,"children":511},{"__ignoreMap":401},[512],{"type":414,"tag":513,"props":514,"children":517},"span",{"class":515,"line":516},"line",1,[518,524,530,534,538,544,549,555,560,564,568,572,577,582,588,592,597,602,607],{"type":414,"tag":513,"props":519,"children":521},{"style":520},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[522],{"type":420,"value":523},"builder",{"type":414,"tag":513,"props":525,"children":527},{"style":526},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[528],{"type":420,"value":529},".",{"type":414,"tag":513,"props":531,"children":532},{"style":520},[533],{"type":420,"value":263},{"type":414,"tag":513,"props":535,"children":536},{"style":526},[537],{"type":420,"value":529},{"type":414,"tag":513,"props":539,"children":541},{"style":540},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[542],{"type":420,"value":543},"AddAzureKeyVault",{"type":414,"tag":513,"props":545,"children":546},{"style":526},[547],{"type":420,"value":548},"(new",{"type":414,"tag":513,"props":550,"children":552},{"style":551},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[553],{"type":420,"value":554}," Uri",{"type":414,"tag":513,"props":556,"children":557},{"style":526},[558],{"type":420,"value":559},"(",{"type":414,"tag":513,"props":561,"children":562},{"style":520},[563],{"type":420,"value":523},{"type":414,"tag":513,"props":565,"children":566},{"style":526},[567],{"type":420,"value":529},{"type":414,"tag":513,"props":569,"children":570},{"style":520},[571],{"type":420,"value":263},{"type":414,"tag":513,"props":573,"children":574},{"style":526},[575],{"type":420,"value":576},"[",{"type":414,"tag":513,"props":578,"children":579},{"style":526},[580],{"type":420,"value":581},"\"",{"type":414,"tag":513,"props":583,"children":585},{"style":584},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[586],{"type":420,"value":587},"KeyVault:VaultUri",{"type":414,"tag":513,"props":589,"children":590},{"style":526},[591],{"type":420,"value":581},{"type":414,"tag":513,"props":593,"children":594},{"style":526},[595],{"type":420,"value":596},"]),",{"type":414,"tag":513,"props":598,"children":599},{"style":526},[600],{"type":420,"value":601}," new",{"type":414,"tag":513,"props":603,"children":604},{"style":551},[605],{"type":420,"value":606}," DefaultAzureCredential",{"type":414,"tag":513,"props":608,"children":609},{"style":526},[610],{"type":420,"value":611},"());\n",{"type":414,"tag":423,"props":613,"children":614},{},[615,617,624,626,632],{"type":420,"value":616},"Configuration from previously registered sources is already available because configuration sources are directly loaded once they are added. If you want to learn more about the behind-the-scenes, Andrew Lock has a ",{"type":414,"tag":468,"props":618,"children":621},{"href":619,"rel":620},"https://andrewlock.net/exploring-dotnet-6-part-1-looking-inside-configurationmanager-in-dotnet-6/",[472],[622],{"type":420,"value":623},"very interesting article",{"type":420,"value":625}," about ",{"type":414,"tag":429,"props":627,"children":629},{"className":628},[],[630],{"type":420,"value":631},"ConfigurationManager",{"type":420,"value":633}," that I suggest you read.",{"type":414,"tag":415,"props":635,"children":637},{"id":636},"the-vs-code-extension-you-should-try-vite",[638],{"type":420,"value":639},"The VS Code extension you should try: Vite",{"type":414,"tag":423,"props":641,"children":642},{},[643,645,652,654,661],{"type":420,"value":644},"If you are developing a front-end using Vite (and there are ",{"type":414,"tag":468,"props":646,"children":649},{"href":647,"rel":648},"https://vitejs.dev/guide/why.html",[472],[650],{"type":420,"value":651},"good reasons",{"type":420,"value":653}," why you should), there is a ",{"type":414,"tag":468,"props":655,"children":658},{"href":656,"rel":657},"https://marketplace.visualstudio.com/items?itemName=antfu.vite",[472],[659],{"type":420,"value":660},"Vite extension for VS Code",{"type":420,"value":662}," currently in preview.",{"type":414,"tag":423,"props":664,"children":665},{},[666],{"type":420,"value":667},"You can see the main features of this extension below: little things that make you always more productive.",{"type":414,"tag":423,"props":669,"children":670},{},[671],{"type":414,"tag":482,"props":672,"children":676},{"alt":673,"className":674,"src":675},"Vs code vite exension.",[486,487],"/posts/images/w032022tips_vscode_vite_1.png",[],{"type":414,"tag":415,"props":678,"children":680},{"id":679},"the-azure-devops-tip-you-did-not-know-about-creating-pull-requests-templates",[681],{"type":420,"value":682},"The Azure DevOps tip you did not know about: creating pull requests templates",{"type":414,"tag":423,"props":684,"children":685},{},[686],{"type":420,"value":687},"As a developer working with Azure DevOps, you probably spend a lot of time reviewing your colleagues pull requests and what helps you to have a good context (of what problem a pull request solves or what new feature it brings) are having a work item associated to the pull request, and having a good description. You can enforce the pull request to have an associated work item by setting it to mandatory in your branch policies, but \"having a good description\" is not something you can enforce.",{"type":414,"tag":423,"props":689,"children":690},{},[691,693,700],{"type":420,"value":692},"However, you can provide some guidance on what the description should tell, how it should be organized, what are the key points to verify before submitting the pull request... How do you do that? By creating a pull request template in your repository which will be a markdown file that will be automatically added to a pull request description when the pull request is created. You can read the ",{"type":414,"tag":468,"props":694,"children":697},{"href":695,"rel":696},"https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-request-templates?view=azure-devops",[472],[698],{"type":420,"value":699},"official documentation",{"type":420,"value":701}," but let me show you quickly how it works:",{"type":414,"tag":423,"props":703,"children":704},{},[705,707,713],{"type":420,"value":706},"1- You create a ",{"type":414,"tag":429,"props":708,"children":710},{"className":709},[],[711],{"type":420,"value":712},".azuredevops",{"type":420,"value":714}," folder in the root of your git repository",{"type":414,"tag":423,"props":716,"children":717},{},[718,720,726],{"type":420,"value":719},"2- You create a markdown file ",{"type":414,"tag":429,"props":721,"children":723},{"className":722},[],[724],{"type":420,"value":725},"pull_request_template.md",{"type":420,"value":727}," containing the description you want",{"type":414,"tag":423,"props":729,"children":730},{},[731],{"type":420,"value":732},"You can see below an example of a template I created:",{"type":414,"tag":423,"props":734,"children":735},{},[736],{"type":414,"tag":482,"props":737,"children":741},{"alt":738,"className":739,"src":740},"Pull request markdown template file.",[486,487],"/posts/images/w032022tips_pr_template_1.png",[],{"type":414,"tag":423,"props":743,"children":744},{},[745],{"type":420,"value":746},"3- You commit this file and push it in your main branch",{"type":414,"tag":423,"props":748,"children":749},{},[750],{"type":420,"value":751},"4- Now when someone creates a pull request, he will have a pre-filled description to complete before submitting his pull request",{"type":414,"tag":423,"props":753,"children":754},{},[755],{"type":420,"value":756},"This is what it looks like for my template:",{"type":414,"tag":423,"props":758,"children":759},{},[760],{"type":414,"tag":482,"props":761,"children":765},{"alt":762,"className":763,"src":764},"Pull request template in Azure DevOps.",[486,487],"/posts/images/w032022tips_pr_template_2.png",[],{"type":414,"tag":767,"props":768,"children":770},"callout",{"icon":769},"i-heroicons-chat-bubble-left-20-solid",[771],{"type":414,"tag":423,"props":772,"children":773},{},[774,776,782],{"type":420,"value":775},"If you are using GitHub and not Azure DevOps, just know that there are also pull requests templates in ",{"type":414,"tag":468,"props":777,"children":780},{"href":778,"rel":779},"https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository",[472],[781],{"type":420,"value":233},{"type":420,"value":529},{"type":414,"tag":415,"props":784,"children":786},{"id":785},"tool-of-the-week-degit",[787],{"type":420,"value":788},"Tool of the week: degit",{"type":414,"tag":423,"props":790,"children":791},{},[792],{"type":420,"value":793},"Sometimes you find an interesting open source git repository that could help you save some time when starting a new project. That can be a project template or a sample, it's a repository that you want to start coding from. You can clone it or fork it but you will retrieve the whole git history and that is something that you probably don't want.",{"type":414,"tag":423,"props":795,"children":796},{},[797,804,806,812,814,820,822,828],{"type":414,"tag":468,"props":798,"children":801},{"href":799,"rel":800},"https://github.com/Rich-Harris/degit",[472],[802],{"type":420,"value":803},"degit",{"type":420,"value":805}," is a helpful tool that solves this issue. When you run this tool on a git repository, it retrieves locally the latest version of this repository without its git history. It's an ",{"type":414,"tag":429,"props":807,"children":809},{"className":808},[],[810],{"type":420,"value":811},"npm",{"type":420,"value":813}," tool so you can install it globally with ",{"type":414,"tag":429,"props":815,"children":817},{"className":816},[],[818],{"type":420,"value":819},"npm install -g degit",{"type":420,"value":821}," or directly run it using ",{"type":414,"tag":429,"props":823,"children":825},{"className":824},[],[826],{"type":420,"value":827},"npx",{"type":420,"value":529},{"type":414,"tag":423,"props":830,"children":831},{},[832],{"type":414,"tag":482,"props":833,"children":837},{"alt":834,"className":835,"src":836},"Ouput when using degit package on a repository.",[486,487],"/posts/images/w032022tips_degit_1.png",[],{"type":414,"tag":423,"props":839,"children":840},{},[841,843,848],{"type":420,"value":842},"Don't hesitate to give the project a star on ",{"type":414,"tag":468,"props":844,"children":846},{"href":799,"rel":845},[472],[847],{"type":420,"value":233},{"type":420,"value":849}," if you find it useful.",{"type":414,"tag":423,"props":851,"children":852},{},[853],{"type":420,"value":854},"And that's it for this week, happy learning!",{"type":414,"tag":856,"props":857,"children":858},"style",{},[859],{"type":420,"value":860},"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":401,"searchDepth":862,"depth":862,"links":863},2,[864,865,866,867],{"id":417,"depth":862,"text":421},{"id":636,"depth":862,"text":639},{"id":679,"depth":862,"text":682},{"id":785,"depth":862,"text":788},"markdown","content:1.posts:33.w03-2022-tips-learned-this-week.md","content","1.posts/33.w03-2022-tips-learned-this-week.md","md",{"_path":37,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":36,"description":874,"lead":875,"date":876,"image":877,"badge":879,"tags":881,"body":882,"_type":868,"_id":1273,"_source":870,"_file":1274,"_extension":872},"The possibility to add configuration sources in Azure Functions has just been released with the latest version of Microsoft.Azure.Functions.Extensions NuGet package. I have been waiting for this feature for a long time (like many people I think) because it brings to Azure Functions all the things we are used to with configuration in ASP.NET Core 😻. But that is not the only reason, it is also because with this feature you almost don't need to use key vault references!","Talking about how to manage configuration secrets in Azure Functions.","2020-09-21T00:00:00.000Z",{"src":878},"/images/lightning_1.jpg",{"label":880},"Development",[257,260,228,263],{"type":411,"children":883,"toc":1265},[884,906,911,917,944,953,967,976,981,987,999,1004,1013,1018,1072,1084,1093,1098,1111,1123,1166,1175,1180,1189,1194,1200,1236,1245,1250,1256,1261],{"type":414,"tag":423,"props":885,"children":886},{},[887,889,895,897,904],{"type":420,"value":888},"The possibility to add configuration sources in Azure Functions has just been released with the latest version of ",{"type":414,"tag":429,"props":890,"children":892},{"className":891},[],[893],{"type":420,"value":894},"Microsoft.Azure.Functions.Extensions",{"type":420,"value":896}," ",{"type":414,"tag":468,"props":898,"children":901},{"href":899,"rel":900},"https://www.nuget.org/packages/Microsoft.Azure.Functions.Extensions/1.1.0",[472],[902],{"type":420,"value":903},"NuGet package",{"type":420,"value":905},". I have been waiting for this feature for a long time (like many people I think) because it brings to Azure Functions all the things we are used to with configuration in ASP.NET Core 😻. But that is not the only reason, it is also because with this feature you almost don't need to use key vault references!",{"type":414,"tag":423,"props":907,"children":908},{},[909],{"type":420,"value":910},"But before deep dive into this topic, let's give a bit of context about configuration and secrets in Azure Functions (just skip the next section if you already are familiar with all that).",{"type":414,"tag":415,"props":912,"children":914},{"id":913},"a-quick-reminder-about-configuration-and-secrets-in-azure-functions",[915],{"type":420,"value":916},"A quick reminder about configuration and secrets in Azure Functions",{"type":414,"tag":423,"props":918,"children":919},{},[920,922,927,929,935,937,942],{"type":420,"value":921},"Configuration used by functions in a Function App is stored in settings that can be set in the ",{"type":414,"tag":429,"props":923,"children":925},{"className":924},[],[926],{"type":420,"value":263},{"type":420,"value":928}," section of a Function App in Azure Portal. When developing locally you have to use a ",{"type":414,"tag":429,"props":930,"children":932},{"className":931},[],[933],{"type":420,"value":934},"local.settings.json",{"type":420,"value":936}," file that will contain copies of the settings stored in Azure portal. The settings from ",{"type":414,"tag":429,"props":938,"children":940},{"className":939},[],[941],{"type":420,"value":934},{"type":420,"value":943}," will be loaded as environment variables when debugging locally. But as its name suggests, the purpose of this file is to be used for local development only: its settings are not used when the function runs on Azure. Furthermore, this file should never be committed to avoid putting settings corresponding to secrets in source control.",{"type":414,"tag":423,"props":945,"children":946},{},[947],{"type":414,"tag":482,"props":948,"children":952},{"alt":949,"className":950,"src":951},"Azure Function local settings file.",[486,487],"/posts/images/functionssecrets_localsettings_1.png",[],{"type":414,"tag":423,"props":954,"children":955},{},[956,958,965],{"type":420,"value":957},"Speaking of secrets, they should never be directly stored in the application settings of a Function App (the same goes for App Services by the way). Why not? Because secrets would be available to  anyone who has access to the Function App in the Azure Portal. The right way is to use an Azure Key Vault which is the Azure component for securely storing and accessing secrets 🔒. Once your secrets are in the key vault, you have to grant the Key Vault access to the identity of your Function App and you can then reference the secrets you need directly in your application settings. These are called ",{"type":414,"tag":468,"props":959,"children":962},{"href":960,"rel":961},"https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references",[472],[963],{"type":420,"value":964},"Key Vault references",{"type":420,"value":966}," because an application setting does not contain directly the value of a secret but a reference to the secret which is stored in Key Vault. When running, your function will automatically have access to the secret and its value as an environment variable, as if it was a normal application setting.",{"type":414,"tag":423,"props":968,"children":969},{},[970],{"type":414,"tag":482,"props":971,"children":975},{"alt":972,"className":973,"src":974},"Function App application settings in Azure portal.",[486,487],"/posts/images/functionssecrets_portal_1.png",[],{"type":414,"tag":423,"props":977,"children":978},{},[979],{"type":420,"value":980},"Key Vault references work for both App Services and Function Apps and are particularly useful for existing applications that have their secrets stored in settings because securing the secrets with Azure Key Vault references does not require any code change.",{"type":414,"tag":415,"props":982,"children":984},{"id":983},"the-downside-of-key-vault-references-the-local-debugging-experience",[985],{"type":420,"value":986},"The downside of Key Vault references: the local debugging experience",{"type":414,"tag":423,"props":988,"children":989},{},[990,992,997],{"type":420,"value":991},"Do you remember when I told you that the local settings file should not be committed to your git repository? Well, what you might not have realized is that it means when someone from your team clones the git repository containing your function he won't have this ",{"type":414,"tag":429,"props":993,"children":995},{"className":994},[],[996],{"type":420,"value":934},{"type":420,"value":998}," file which is mandatory to run your function app locally. And even if he creates manually the file, he will not necessarily know which settings to put in it. But we want to avoid him manually copying all the settings from the Azure portal or asking a colleague to send his local settings file by email (which is a really bad practice if it contains secrets). Hopefully, there are some ways to fill or generate this file.",{"type":414,"tag":423,"props":1000,"children":1001},{},[1002],{"type":420,"value":1003},"If you use Visual Studio there is a GUI that will help you compare/modify local settings and Azure settings.",{"type":414,"tag":423,"props":1005,"children":1006},{},[1007],{"type":414,"tag":482,"props":1008,"children":1012},{"alt":1009,"className":1010,"src":1011},"Function App application settings in Visual Studio.",[486,487],"/posts/images/functionssecrets_vs_1.png",[],{"type":414,"tag":423,"props":1014,"children":1015},{},[1016],{"type":420,"value":1017},"And whether or not you are using Visual Studio, you can generate the local settings file filled with Azure settings with a few Azure Functions CLI commands:",{"type":414,"tag":504,"props":1019,"children":1023},{"className":1020,"code":1021,"language":1022,"meta":401,"style":401},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","func azure functionapp fetch-app-settings $FUNCTION_APP_NAME\nfunc settings decrypt\n","bash",[1024],{"type":414,"tag":429,"props":1025,"children":1026},{"__ignoreMap":401},[1027,1055],{"type":414,"tag":513,"props":1028,"children":1029},{"class":515,"line":516},[1030,1035,1040,1045,1050],{"type":414,"tag":513,"props":1031,"children":1032},{"style":551},[1033],{"type":420,"value":1034},"func",{"type":414,"tag":513,"props":1036,"children":1037},{"style":584},[1038],{"type":420,"value":1039}," azure",{"type":414,"tag":513,"props":1041,"children":1042},{"style":584},[1043],{"type":420,"value":1044}," functionapp",{"type":414,"tag":513,"props":1046,"children":1047},{"style":584},[1048],{"type":420,"value":1049}," fetch-app-settings",{"type":414,"tag":513,"props":1051,"children":1052},{"style":520},[1053],{"type":420,"value":1054}," $FUNCTION_APP_NAME\n",{"type":414,"tag":513,"props":1056,"children":1057},{"class":515,"line":862},[1058,1062,1067],{"type":414,"tag":513,"props":1059,"children":1060},{"style":551},[1061],{"type":420,"value":1034},{"type":414,"tag":513,"props":1063,"children":1064},{"style":584},[1065],{"type":420,"value":1066}," settings",{"type":414,"tag":513,"props":1068,"children":1069},{"style":584},[1070],{"type":420,"value":1071}," decrypt\n",{"type":414,"tag":423,"props":1073,"children":1074},{},[1075,1077,1082],{"type":420,"value":1076},"This is an example of a generated ",{"type":414,"tag":429,"props":1078,"children":1080},{"className":1079},[],[1081],{"type":420,"value":934},{"type":420,"value":1083}," file:",{"type":414,"tag":423,"props":1085,"children":1086},{},[1087],{"type":414,"tag":482,"props":1088,"children":1092},{"alt":1089,"className":1090,"src":1091},"Generated local settings file.",[486,487],"/posts/images/functionssecrets_localsettings_2.png",[],{"type":414,"tag":423,"props":1094,"children":1095},{},[1096],{"type":420,"value":1097},"However, as you can see, the settings corresponding to secrets contain the Key Vault reference values that are used by Azure to link the settings to the secrets. But this is an Azure mechanism, locally the true secrets value won't be loaded into configuration. So you will have to manually retrieve the value of the secrets in your key vault and set them manually in your local settings file. That may be okay for one secret but that gets quickly annoying when you have many secrets. You don't want your team members to constantly lose time copying secret values from the key vault on their local environment. I don't even talk about the loss of time understanding what is wrong when a secret value has changed and you did not realize it or the bad habits it could lead to like sending secrets by email or chat messages.",{"type":414,"tag":423,"props":1099,"children":1100},{},[1101,1103,1110],{"type":420,"value":1102},"This is a terrible local debugging experience and honestly, you don't want that. What you want is that your function code just works when you or one of your colleagues clones or pulls a new version of the function app code. When debugging locally the code of an ASP.NET Core application deployed in an App Service you don't have this kind of problem because usually your code directly loads the secrets from the Key Vault thanks to ",{"type":414,"tag":468,"props":1104,"children":1107},{"href":1105,"rel":1106},"https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1",[472],[1108],{"type":420,"value":1109},"Key Vault configuration provider",{"type":420,"value":529},{"type":414,"tag":415,"props":1112,"children":1114},{"id":1113},"here-comes-ifunctionsconfigurationbuilder",[1115,1117],{"type":420,"value":1116},"Here comes ",{"type":414,"tag":429,"props":1118,"children":1120},{"className":1119},[],[1121],{"type":420,"value":1122},"IFunctionsConfigurationBuilder",{"type":414,"tag":423,"props":1124,"children":1125},{},[1126,1128,1133,1135,1141,1143,1150,1152,1157,1159,1164],{"type":420,"value":1127},"If you are already familiar with dependency injection in Azure Functions, you already know the ",{"type":414,"tag":429,"props":1129,"children":1131},{"className":1130},[],[1132],{"type":420,"value":894},{"type":420,"value":1134}," NuGet package that allows you to inherit from the ",{"type":414,"tag":429,"props":1136,"children":1138},{"className":1137},[],[1139],{"type":420,"value":1140},"FunctionStartup",{"type":420,"value":1142}," abstract class and register the different services you want to inject into your functions (you can find more about that in the ",{"type":414,"tag":468,"props":1144,"children":1147},{"href":1145,"rel":1146},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection",[472],[1148],{"type":420,"value":1149},"documentation",{"type":420,"value":1151},"). In the latest version of this NuGet package, a new virtual method has been added to ",{"type":414,"tag":429,"props":1153,"children":1155},{"className":1154},[],[1156],{"type":420,"value":1140},{"type":420,"value":1158},": ",{"type":414,"tag":429,"props":1160,"children":1162},{"className":1161},[],[1163],{"type":420,"value":450},{"type":420,"value":1165},". It allows you to specify additional configuration sources you would need in your functions.",{"type":414,"tag":423,"props":1167,"children":1168},{},[1169],{"type":414,"tag":482,"props":1170,"children":1174},{"alt":1171,"className":1172,"src":1173},"FunctionsStartup abstract class.",[486,487],"/posts/images/functionssecrets_vs_2.png",[],{"type":414,"tag":423,"props":1176,"children":1177},{},[1178],{"type":420,"value":1179},"What is awesome is that you can use all the configuration providers you are used to in ASP.NET Core and that includes the Key Vault Configuration provider. I think you understand what I am getting at 😉: instead of using key vault references in your function app settings, you can simply retrieve the secrets from your key vault thanks to the configuration provider.",{"type":414,"tag":423,"props":1181,"children":1182},{},[1183],{"type":414,"tag":482,"props":1184,"children":1188},{"alt":1185,"className":1186,"src":1187},"Debugging configuration in Startup file in Visual Studio.",[486,487],"/posts/images/functionssecrets_vs_3.png",[],{"type":414,"tag":423,"props":1190,"children":1191},{},[1192],{"type":420,"value":1193},"This way, no more copying secret, no more storing secrets values locally, no more wondering if you have the latest version of a secret. Say goodbye to key vault references, pull the latest version of your code, press F5 and it will work!",{"type":414,"tag":415,"props":1195,"children":1197},{"id":1196},"the-triggers-case",[1198],{"type":420,"value":1199},"The triggers case",{"type":414,"tag":423,"props":1201,"children":1202},{},[1203,1205,1219,1221,1225,1227,1234],{"type":420,"value":1204},"Well in my title I said ",{"type":414,"tag":1206,"props":1207,"children":1208},"em",{},[1209,1211,1217],{"type":420,"value":1210},"\"you ",{"type":414,"tag":1212,"props":1213,"children":1214},"strong",{},[1215],{"type":420,"value":1216},"almost",{"type":420,"value":1218}," no longer need key vault references\"",{"type":420,"value":1220}," and the ",{"type":414,"tag":1212,"props":1222,"children":1223},{},[1224],{"type":420,"value":1216},{"type":420,"value":1226}," is important. As the ",{"type":414,"tag":468,"props":1228,"children":1231},{"href":1229,"rel":1230},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources",[472],[1232],{"type":420,"value":1233},"Azure Functions documentation",{"type":420,"value":1235}," about customizing configuration sources mentions:",{"type":414,"tag":767,"props":1237,"children":1239},{"icon":1238},"i-heroicons-light-bulb",[1240],{"type":414,"tag":423,"props":1241,"children":1242},{},[1243],{"type":420,"value":1244},"For function apps running in the Consumption or Premium plans, modifications to configuration values used in triggers can cause scaling errors. Any changes to these properties by the FunctionsStartup class result in a function app startup error.",{"type":414,"tag":423,"props":1246,"children":1247},{},[1248],{"type":420,"value":1249},"Therefore, if you use a trigger that needs a secret (the connection string of an EventHub trigger for instance), you have no other choice than to use a key vault reference. But for everything else you are good to go with Azure Key Vault configuration provider.",{"type":414,"tag":415,"props":1251,"children":1253},{"id":1252},"to-conclude",[1254],{"type":420,"value":1255},"To conclude",{"type":414,"tag":423,"props":1257,"children":1258},{},[1259],{"type":420,"value":1260},"To summarize, after a quick recall of how Azure Functions configuration works we have seen how Key Vault references can help to avoid having secret values in settings. We talked about the downside of this approach for the local development experience and how using the Azure Key Vault configuration provider solved that except when a secret is needed in a trigger.",{"type":414,"tag":856,"props":1262,"children":1263},{},[1264],{"type":420,"value":860},{"title":401,"searchDepth":862,"depth":862,"links":1266},[1267,1268,1269,1271,1272],{"id":913,"depth":862,"text":916},{"id":983,"depth":862,"text":986},{"id":1113,"depth":862,"text":1270},"Here comes IFunctionsConfigurationBuilder",{"id":1196,"depth":862,"text":1199},{"id":1252,"depth":862,"text":1255},"content:1.posts:10.azure-functions-custom-configuration.md","1.posts/10.azure-functions-custom-configuration.md",1716749600706]